mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 11:39:25 +00:00
multisig [DO NOT USE IN PRODUCTION]
This commit is contained in:
parent
007933f2e0
commit
6df4693cf0
132 changed files with 7521 additions and 364 deletions
|
@ -284,10 +284,13 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(STATIC)
|
if(STATIC)
|
||||||
# add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
|
||||||
# add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
||||||
|
elseif (NOT (APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY))
|
||||||
|
if(NOT "${ARCH}" STREQUAL "armv7-a")
|
||||||
|
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
2
monero
2
monero
|
@ -1 +1 @@
|
||||||
Subproject commit 892a9a16d169ecd8f15e892f1e14fdd84cfce0f3
|
Subproject commit f82b6889af63e7d0fbed0b3f7032f0c2924cf812
|
|
@ -20,8 +20,8 @@ endif()
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||||
|
|
||||||
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
#set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||||
add_subdirectory(third-party/singleapplication)
|
#add_subdirectory(third-party/singleapplication)
|
||||||
|
|
||||||
if (CHECK_UPDATES)
|
if (CHECK_UPDATES)
|
||||||
add_subdirectory(openpgp)
|
add_subdirectory(openpgp)
|
||||||
|
@ -55,6 +55,8 @@ file(GLOB SOURCE_FILES
|
||||||
"widgets/*.cpp"
|
"widgets/*.cpp"
|
||||||
"wizard/*.h"
|
"wizard/*.h"
|
||||||
"wizard/*.cpp"
|
"wizard/*.cpp"
|
||||||
|
"wizard/multisig/*.h"
|
||||||
|
"wizard/multisig/*.cpp"
|
||||||
"wallet/*.h"
|
"wallet/*.h"
|
||||||
"wallet/*.cpp"
|
"wallet/*.cpp"
|
||||||
"qrcode/*.h"
|
"qrcode/*.h"
|
||||||
|
@ -272,7 +274,7 @@ target_link_libraries(feather PRIVATE
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
${QRENCODE_LIBRARY}
|
${QRENCODE_LIBRARY}
|
||||||
${POLYSEED_LIBRARY}
|
${POLYSEED_LIBRARY}
|
||||||
SingleApplication::SingleApplication
|
# SingleApplication::SingleApplication
|
||||||
${ICU_LIBRARIES}
|
${ICU_LIBRARIES}
|
||||||
${LIBZIP_LIBRARIES}
|
${LIBZIP_LIBRARIES}
|
||||||
${ZLIB_LIBRARIES}
|
${ZLIB_LIBRARIES}
|
||||||
|
|
|
@ -89,11 +89,8 @@ void CoinsWidget::setModel(CoinsModel * model, Coins * coins) {
|
||||||
ui->coins->setColumnHidden(CoinsModel::SpentHeight, true);
|
ui->coins->setColumnHidden(CoinsModel::SpentHeight, true);
|
||||||
ui->coins->setColumnHidden(CoinsModel::Frozen, true);
|
ui->coins->setColumnHidden(CoinsModel::Frozen, true);
|
||||||
|
|
||||||
if (!m_wallet->viewOnly()) {
|
bool showKeyImageKnown = m_wallet->viewOnly() || m_wallet->isMultisig();
|
||||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, true);
|
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, !showKeyImageKnown);
|
||||||
} else {
|
|
||||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
ui->coins->header()->setSectionResizeMode(CoinsModel::Label, QHeaderView::Stretch);
|
ui->coins->header()->setSectionResizeMode(CoinsModel::Label, QHeaderView::Stretch);
|
||||||
|
@ -335,6 +332,11 @@ bool CoinsWidget::isCoinSpendable(CoinsInfo *coin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!coin->haveMultisigK()) {
|
||||||
|
Utils::showError(this, "Unable to spend outputs", "We already spent this output in another transaction proposal, other participants would not be able to sign both transactions");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (coin->spent()) {
|
if (coin->spent()) {
|
||||||
Utils::showError(this, "Unable to spend outputs", "Selected output was already spent");
|
Utils::showError(this, "Unable to spend outputs", "Selected output was already spent");
|
||||||
return false;
|
return false;
|
||||||
|
|
152
src/MMSWidget.cpp
Normal file
152
src/MMSWidget.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "MMSWidget.h"
|
||||||
|
#include "ui_MMSWidget.h"
|
||||||
|
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
#include "dialog/OutputInfoDialog.h"
|
||||||
|
#include "dialog/OutputSweepDialog.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
MMSWidget::MMSWidget(Wallet *wallet, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::MMSWidget)
|
||||||
|
, m_wallet(wallet)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->btn_mmsNext, &QPushButton::clicked, [this]{
|
||||||
|
m_store->next();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_mmsNextSync, &QPushButton::clicked, [this]{
|
||||||
|
m_store->next(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_exportMultisig, &QPushButton::clicked, [this]{
|
||||||
|
m_store->exportMultisig();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_deleteAll, &QPushButton::clicked, [this]{
|
||||||
|
m_store->deleteAllMessages();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->mms->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(ui->mms, &QTreeView::customContextMenuRequested, this, &MMSWidget::showContextMenu);
|
||||||
|
connect(ui->transactionProposals, &QTreeView::customContextMenuRequested, this, &MMSWidget::showContexxtMenu);
|
||||||
|
|
||||||
|
connect(m_wallet->mmsStore(), &MultisigMessageStore::connectionError, [this]{
|
||||||
|
Utils::showError(this, "Unable to connect to message service");
|
||||||
|
});
|
||||||
|
|
||||||
|
// ui->tabWidget->setTabVisible(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMSWidget::setModel(MultisigMessageModel * model, MultisigIncomingTxModel * incomingModel, MultisigMessageStore * store) {
|
||||||
|
m_store = store;
|
||||||
|
m_model = model;
|
||||||
|
ui->mms->setModel(m_model);
|
||||||
|
|
||||||
|
ui->mms->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
ui->mms->setSortingEnabled(true);
|
||||||
|
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::Modified, true);
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::Sent, true);
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::Hash, true);
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::TransportId, true);
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::WalletHeight, true);
|
||||||
|
ui->mms->setColumnHidden(MultisigMessageModel::SignatureCount, true);
|
||||||
|
|
||||||
|
ui->transactionProposals->setModel(incomingModel);
|
||||||
|
ui->transactionProposals->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
ui->transactionProposals->setSortingEnabled(true);
|
||||||
|
ui->transactionProposals->setColumnHidden(MultisigIncomingTxModel::MessageID, true);
|
||||||
|
ui->transactionProposals->setColumnHidden(MultisigIncomingTxModel::TxCount, true);
|
||||||
|
ui->transactionProposals->setColumnHidden(MultisigIncomingTxModel::TxId, true);
|
||||||
|
ui->transactionProposals->setColumnHidden(MultisigIncomingTxModel::Signatures, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMSWidget::showContextMenu(const QPoint &point) {
|
||||||
|
auto* menu = new QMenu(this);
|
||||||
|
|
||||||
|
menu->addAction("Sign transaction", [this, point]{
|
||||||
|
quint32 id = this->idAtPoint(ui->mms, point);
|
||||||
|
m_store->signTx(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction("Delete message", [this, point]{
|
||||||
|
quint32 id = this->idAtPoint(ui->mms, point);
|
||||||
|
m_store->deleteMessage(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction("Copy message", [this, point]{
|
||||||
|
quint32 id = this->idAtPoint(ui->mms, point);
|
||||||
|
std::string content = m_store->exportMessage(id);
|
||||||
|
Utils::copyToClipboard(QString::fromStdString(content));
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction("Export message", [this, point]{
|
||||||
|
quint32 id = this->idAtPoint(ui->mms, point);
|
||||||
|
std::string content = m_store->exportMessage(id);
|
||||||
|
|
||||||
|
QString defaultName = QString("mms_message_content_%1.mms").arg(QString::number(id));
|
||||||
|
QString fn = Utils::getSaveFileName(this, "Save key images to file", defaultName, "Key Images (*_keyImages)");
|
||||||
|
if (fn.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file{fn};
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
Utils::showError(this, "Failed to export MMS message", QString("Could not open file %1 for writing").arg(fn));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(content.data(), content.size());
|
||||||
|
file.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->popup(ui->mms->viewport()->mapToGlobal(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMSWidget::showContexxtMenu(const QPoint &point) {
|
||||||
|
auto* menu = new QMenu(this);
|
||||||
|
|
||||||
|
menu->addAction("Review transaction", [this, point]{
|
||||||
|
quint32 id = iddAtPoint(point);
|
||||||
|
m_store->signTx(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->addAction("Remove transaction", [this, point]{
|
||||||
|
quint32 id = iddAtPoint(point);
|
||||||
|
m_store->deleteMessage(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu->popup(ui->transactionProposals->viewport()->mapToGlobal(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 MMSWidget::iddAtPoint(const QPoint &point) {
|
||||||
|
QModelIndex index = ui->transactionProposals->indexAt(point);
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex dataIndex = index.sibling(index.row(), MultisigIncomingTxModel::MessageID);
|
||||||
|
|
||||||
|
return dataIndex.data().toUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 MMSWidget::idAtPoint(QTreeView *tree, const QPoint &point) {
|
||||||
|
QModelIndex index = tree->indexAt(point);
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex dataIndex = index.sibling(index.row(), MultisigMessageModel::Id);
|
||||||
|
return dataIndex.data().toUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
MMSWidget::~MMSWidget() = default;
|
44
src/MMSWidget.h
Normal file
44
src/MMSWidget.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// Created by user on 1/3/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FEATHER_MMSWIDGET_H
|
||||||
|
#define FEATHER_MMSWIDGET_H
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QSvgWidget>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
#include "model/MultisigMessageModel.h"
|
||||||
|
#include "model/MultisigIncomingTxModel.h"
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
#include "libwalletqt/Wallet.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MMSWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MMSWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MMSWidget(Wallet *wallet, QWidget *parent = nullptr);
|
||||||
|
void setModel(MultisigMessageModel *model, MultisigIncomingTxModel *incomingTxModel, MultisigMessageStore *store);
|
||||||
|
~MMSWidget() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
quint32 idAtPoint(QTreeView *tree, const QPoint &point);
|
||||||
|
quint32 iddAtPoint(const QPoint &point);
|
||||||
|
void showContextMenu(const QPoint &point);
|
||||||
|
void showContexxtMenu(const QPoint &point);
|
||||||
|
|
||||||
|
QScopedPointer<Ui::MMSWidget> ui;
|
||||||
|
Wallet *m_wallet;
|
||||||
|
|
||||||
|
MultisigMessageStore *m_store;
|
||||||
|
MultisigMessageModel * m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_MMSWIDGET_H
|
112
src/MMSWidget.ui
Normal file
112
src/MMSWidget.ui
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MMSWidget</class>
|
||||||
|
<widget class="QWidget" name="MMSWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>857</width>
|
||||||
|
<height>464</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="tab_2">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Transaction proposals</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="transactionProposals">
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="rootIsDecorated">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Message store</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="mms"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_mmsNext">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_mmsNextSync">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next (force sync)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_exportMultisig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export multisig</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_deleteAll">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -239,6 +239,10 @@ void MainWindow::initWidgets() {
|
||||||
m_coinsWidget = new CoinsWidget(m_wallet, this);
|
m_coinsWidget = new CoinsWidget(m_wallet, this);
|
||||||
ui->coinsWidgetLayout->addWidget(m_coinsWidget);
|
ui->coinsWidgetLayout->addWidget(m_coinsWidget);
|
||||||
|
|
||||||
|
// [MMS]
|
||||||
|
m_mmsWidget = new MMSWidget(m_wallet, this);
|
||||||
|
ui->mmsWidgetLayout->addWidget(m_mmsWidget);
|
||||||
|
|
||||||
// [Plugins..]
|
// [Plugins..]
|
||||||
for (auto* plugin : m_plugins) {
|
for (auto* plugin : m_plugins) {
|
||||||
if (!plugin->hasParent()) {
|
if (!plugin->hasParent()) {
|
||||||
|
@ -485,6 +489,7 @@ void MainWindow::initWalletContext() {
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(m_wallet, &Wallet::multiBroadcast, this, &MainWindow::onMultiBroadcast);
|
connect(m_wallet, &Wallet::multiBroadcast, this, &MainWindow::onMultiBroadcast);
|
||||||
|
connect(m_wallet->mmsStore(), &MultisigMessageStore::askToSign, this, &MainWindow::onAskToSign);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::menuToggleTabVisible(const QString &key){
|
void MainWindow::menuToggleTabVisible(const QString &key){
|
||||||
|
@ -523,6 +528,8 @@ QString MainWindow::walletKeysPath() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onWalletOpened() {
|
void MainWindow::onWalletOpened() {
|
||||||
|
qDebug() << this->thread();
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
m_splashDialog->hide();
|
m_splashDialog->hide();
|
||||||
|
|
||||||
|
@ -553,6 +560,13 @@ void MainWindow::onWalletOpened() {
|
||||||
m_coinsWidget->setModel(m_wallet->coinsModel(), m_wallet->coins());
|
m_coinsWidget->setModel(m_wallet->coinsModel(), m_wallet->coins());
|
||||||
m_wallet->coinsModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount());
|
m_wallet->coinsModel()->setCurrentSubaddressAccount(m_wallet->currentSubaddressAccount());
|
||||||
|
|
||||||
|
// mms page
|
||||||
|
if (m_wallet->isMultisig()) {
|
||||||
|
m_wallet->mmsStore()->refresh();
|
||||||
|
m_mmsWidget->setModel(m_wallet->mmsModel(), m_wallet->msIncomingTxModel(), m_wallet->mmsStore());
|
||||||
|
m_wallet->setMMSRefreshEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Coin labeling uses set_tx_note, so we need to refresh history too
|
// Coin labeling uses set_tx_note, so we need to refresh history too
|
||||||
connect(m_wallet->coins(), &Coins::descriptionChanged, [this] {
|
connect(m_wallet->coins(), &Coins::descriptionChanged, [this] {
|
||||||
m_wallet->history()->refresh();
|
m_wallet->history()->refresh();
|
||||||
|
@ -695,6 +709,47 @@ void MainWindow::onMultiBroadcast(const QMap<QString, QString> &txHexMap) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::onAskToSign(PendingTransaction *tx) {
|
||||||
|
QString address = tx->destinationAddresses(0)[0];
|
||||||
|
|
||||||
|
TxConfAdvDialog dialog_adv{m_wallet, m_wallet->tmpTxDescription, this};
|
||||||
|
dialog_adv.setMultisigTransaction(tx);
|
||||||
|
dialog_adv.exec();
|
||||||
|
|
||||||
|
// TODO: UNSAFE
|
||||||
|
m_wallet->mmsStore()->refresh(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
TxConfDialog dialog{m_wallet, tx, address, m_wallet->tmpTxDescription, this};
|
||||||
|
switch (dialog.exec()) {
|
||||||
|
case QDialog::Rejected:
|
||||||
|
{
|
||||||
|
if (!dialog.showAdvanced) {
|
||||||
|
m_wallet->disposeTransaction(tx);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QDialog::Accepted:
|
||||||
|
{
|
||||||
|
tx->signMultisigTx();
|
||||||
|
if (tx->status() != PendingTransaction::Status_Ok) {
|
||||||
|
Utils::showError(this, "Unable to sign multisig transaction", tx->errorString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wallet->commitTransaction(tx, m_wallet->tmpTxDescription);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog.showAdvanced) {
|
||||||
|
TxConfAdvDialog dialog_adv{m_wallet, m_wallet->tmpTxDescription, this};
|
||||||
|
dialog_adv.setTransaction(tx);
|
||||||
|
dialog_adv.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onSyncStatus(quint64 height, quint64 target, bool daemonSync) {
|
void MainWindow::onSyncStatus(quint64 height, quint64 target, bool daemonSync) {
|
||||||
if (height >= (target - 1)) {
|
if (height >= (target - 1)) {
|
||||||
this->updateNetStats();
|
this->updateNetStats();
|
||||||
|
@ -957,8 +1012,17 @@ void MainWindow::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QDialog::Accepted:
|
case QDialog::Accepted:
|
||||||
m_wallet->commitTransaction(tx, m_wallet->tmpTxDescription);
|
{
|
||||||
|
if (m_wallet->isMultisig() && m_wallet->multisigThreshold() > 1) {
|
||||||
|
quint32 id = tx->saveToMMS();
|
||||||
|
m_wallet->mmsStore()->sendPendingTransaction(id, dialog.getMultisigSignerIndex());
|
||||||
|
m_wallet->coins()->refresh();
|
||||||
|
m_sendWidget->clearFields();
|
||||||
|
} else {
|
||||||
|
m_wallet->commitTransaction(tx, m_wallet->tmpTxDescription);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dialog.showAdvanced) {
|
if (dialog.showAdvanced) {
|
||||||
|
@ -1037,7 +1101,7 @@ void MainWindow::showSeedDialog() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_wallet->isDeterministic()) {
|
if (!m_wallet->isDeterministic() && !m_wallet->isMultisig()) {
|
||||||
Utils::showInfo(this, "Seed unavailable", "Wallet is non-deterministic and has no seed",
|
Utils::showInfo(this, "Seed unavailable", "Wallet is non-deterministic and has no seed",
|
||||||
{"To obtain wallet keys go to Wallet -> Keys"}, "show_wallet_seed");
|
{"To obtain wallet keys go to Wallet -> Keys"}, "show_wallet_seed");
|
||||||
return;
|
return;
|
||||||
|
@ -1047,6 +1111,12 @@ void MainWindow::showSeedDialog() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_wallet->isMultisig() && m_wallet->hasMultisigPartialKeyImages()) {
|
||||||
|
Utils::copyToClipboard(m_wallet->getMultisigSeed());
|
||||||
|
Utils::showInfo(this, "Multisig seed copied to clipboard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SeedDialog dialog{m_wallet, this};
|
SeedDialog dialog{m_wallet, this};
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
@ -1682,6 +1752,10 @@ void MainWindow::updateTitle() {
|
||||||
title += " [view-only]";
|
title += " [view-only]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_wallet->isMultisig()) {
|
||||||
|
title += " [multisig]";
|
||||||
|
}
|
||||||
|
|
||||||
title += " - Feather";
|
title += " - Feather";
|
||||||
|
|
||||||
this->setWindowTitle(title);
|
this->setWindowTitle(title);
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "SendWidget.h"
|
#include "SendWidget.h"
|
||||||
#include "ReceiveWidget.h"
|
#include "ReceiveWidget.h"
|
||||||
#include "CoinsWidget.h"
|
#include "CoinsWidget.h"
|
||||||
|
#include "MMSWidget.h"
|
||||||
|
|
||||||
#include "WindowManager.h"
|
#include "WindowManager.h"
|
||||||
#include "plugins/Plugin.h"
|
#include "plugins/Plugin.h"
|
||||||
|
@ -165,6 +166,7 @@ private slots:
|
||||||
void onProxySettingsChanged();
|
void onProxySettingsChanged();
|
||||||
void onOfflineMode(bool offline);
|
void onOfflineMode(bool offline);
|
||||||
void onMultiBroadcast(const QMap<QString, QString> &txHexMap);
|
void onMultiBroadcast(const QMap<QString, QString> &txHexMap);
|
||||||
|
void onAskToSign(PendingTransaction *tx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend WindowManager;
|
friend WindowManager;
|
||||||
|
@ -220,6 +222,7 @@ private:
|
||||||
SendWidget *m_sendWidget = nullptr;
|
SendWidget *m_sendWidget = nullptr;
|
||||||
ReceiveWidget *m_receiveWidget = nullptr;
|
ReceiveWidget *m_receiveWidget = nullptr;
|
||||||
CoinsWidget *m_coinsWidget = nullptr;
|
CoinsWidget *m_coinsWidget = nullptr;
|
||||||
|
MMSWidget *m_mmsWidget = nullptr;
|
||||||
|
|
||||||
QPointer<QAction> m_clearRecentlyOpenAction;
|
QPointer<QAction> m_clearRecentlyOpenAction;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
<widget class="QTabWidget" name="tabWidget">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>4</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="iconSize">
|
<property name="iconSize">
|
||||||
<size>
|
<size>
|
||||||
|
@ -158,6 +158,20 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="tab">
|
||||||
|
<attribute name="icon">
|
||||||
|
<iconset resource="assets.qrc">
|
||||||
|
<normaloff>:/assets/images/sign.png</normaloff>:/assets/images/sign.png</iconset>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Multisig</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="mmsWidgetLayout"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -243,6 +243,9 @@ void SendWidget::sendClicked() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: force key image sync here for multisig wallets?
|
||||||
|
// if (m_wallet->isMultisig() && m_wallet->mul)
|
||||||
|
|
||||||
m_wallet->createTransaction(recipient, amount, description, sendAll);
|
m_wallet->createTransaction(recipient, amount, description, sendAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ WindowManager::WindowManager(QObject *parent)
|
||||||
m_splashDialog = new SplashDialog();
|
m_splashDialog = new SplashDialog();
|
||||||
m_cleanupThread = new QThread(this);
|
m_cleanupThread = new QThread(this);
|
||||||
|
|
||||||
connect(m_walletManager, &WalletManager::walletOpened, this, &WindowManager::onWalletOpened);
|
connect(m_walletManager, &WalletManager::walletOpened, this, &WindowManager::onWalletOpened);
|
||||||
connect(m_walletManager, &WalletManager::walletCreated, this, &WindowManager::onWalletCreated);
|
connect(m_walletManager, &WalletManager::walletCreated, this, &WindowManager::onWalletCreated);
|
||||||
connect(m_walletManager, &WalletManager::deviceButtonRequest, this, &WindowManager::onDeviceButtonRequest);
|
connect(m_walletManager, &WalletManager::deviceButtonRequest, this, &WindowManager::onDeviceButtonRequest);
|
||||||
connect(m_walletManager, &WalletManager::deviceButtonPressed, this, &WindowManager::onDeviceButtonPressed);
|
connect(m_walletManager, &WalletManager::deviceButtonPressed, this, &WindowManager::onDeviceButtonPressed);
|
||||||
|
@ -75,6 +75,16 @@ void WindowManager::quitAfterLastWindow() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_openingWallet) {
|
||||||
|
qDebug() << "We're openening a wallet, don't quit application";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_wizard && m_wizard->isVisible()) {
|
||||||
|
qDebug() << "This wizard is still open, don't quit application";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "No wizards in progress and no wallets open, quitting application.";
|
qDebug() << "No wizards in progress and no wallets open, quitting application.";
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
|
@ -85,10 +95,18 @@ void WindowManager::close() {
|
||||||
window->close();
|
window->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wizard->deleteLater();
|
if (m_splashDialog) {
|
||||||
m_splashDialog->deleteLater();
|
m_splashDialog->deleteLater();
|
||||||
m_tray->deleteLater();
|
}
|
||||||
m_docsDialog->deleteLater();
|
if (m_tray) {
|
||||||
|
m_tray->deleteLater();
|
||||||
|
}
|
||||||
|
if (m_wizard) {
|
||||||
|
m_wizard->deleteLater();
|
||||||
|
}
|
||||||
|
if (m_docsDialog) {
|
||||||
|
m_docsDialog->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
torManager()->stop();
|
torManager()->stop();
|
||||||
|
|
||||||
|
@ -296,10 +314,28 @@ void WindowManager::onWalletOpened(Wallet *wallet) {
|
||||||
|
|
||||||
m_splashDialog->hide();
|
m_splashDialog->hide();
|
||||||
m_openWalletTriedOnce = false;
|
m_openWalletTriedOnce = false;
|
||||||
|
|
||||||
|
QString multisigSetup = wallet->getCacheAttribute("feather.multisig_setup");
|
||||||
|
if (multisigSetup == "started") {
|
||||||
|
qDebug() << "Multisig setup in progress, but not finished.";
|
||||||
|
m_openingWallet = false;
|
||||||
|
this->showWizard(WalletWizard::Page_MultisigCreateSetupKey, wallet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (multisigSetup == "configured") {
|
||||||
|
qDebug() << "Multisig setup configured, but not completed";
|
||||||
|
m_openingWallet = false;
|
||||||
|
this->showWizard(WalletWizard::Page_MultisigSetupWallet, wallet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "creating new mainwindow";
|
||||||
auto *window = new MainWindow(this, wallet);
|
auto *window = new MainWindow(this, wallet);
|
||||||
m_windows.append(window);
|
m_windows.append(window);
|
||||||
this->buildTrayMenu();
|
|
||||||
m_openingWallet = false;
|
m_openingWallet = false;
|
||||||
|
|
||||||
|
this->buildTrayMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
void WindowManager::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||||
|
@ -338,7 +374,7 @@ bool WindowManager::autoOpenWallet() {
|
||||||
// ######################## WALLET CREATION ########################
|
// ######################## WALLET CREATION ########################
|
||||||
|
|
||||||
void WindowManager::tryCreateWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage,
|
void WindowManager::tryCreateWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage,
|
||||||
const QString &seedOffset, const QString &subaddressLookahead, bool newWallet) {
|
const QString &seedOffset, const QString &subaddressLookahead, bool newWallet, bool giveToWizard) {
|
||||||
if (Utils::fileExists(path)) {
|
if (Utils::fileExists(path)) {
|
||||||
this->handleWalletError({nullptr, Utils::ERROR, "Failed to create wallet", QString("File already exists: %1").arg(path)});
|
this->handleWalletError({nullptr, Utils::ERROR, "Failed to create wallet", QString("File already exists: %1").arg(path)});
|
||||||
return;
|
return;
|
||||||
|
@ -366,7 +402,14 @@ void WindowManager::tryCreateWallet(Seed seed, const QString &path, const QStrin
|
||||||
wallet->setCacheAttribute("feather.seedoffset", seedOffset);
|
wallet->setCacheAttribute("feather.seedoffset", seedOffset);
|
||||||
|
|
||||||
if (newWallet) {
|
if (newWallet) {
|
||||||
wallet->setNewWallet();
|
wallet->setNewWallet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (giveToWizard) {
|
||||||
|
qDebug() << "Giving wallet to wizard instead of opening";
|
||||||
|
m_wizard->setWallet(wallet);
|
||||||
|
m_openingWallet = false;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->onWalletOpened(wallet);
|
this->onWalletOpened(wallet);
|
||||||
|
@ -417,6 +460,26 @@ void WindowManager::tryCreateWalletFromKeys(const QString &path, const QString &
|
||||||
this->onWalletOpened(wallet);
|
this->onWalletOpened(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::tryRestoreMultisigWallet(const QString &path, const QString &password, const QString &multisigSeed,
|
||||||
|
const QString &mmsRecovery, quint64 restoreHeight, const QString &subaddressLookahead)
|
||||||
|
{
|
||||||
|
if (Utils::fileExists(path)) {
|
||||||
|
this->handleWalletError({nullptr, Utils::ERROR, "Failed to create wallet", QString("File already exists: %1").arg(path)});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multisigSeed.isEmpty()) {
|
||||||
|
this->handleWalletError({nullptr, Utils::ERROR, "Failed to create wallet", "Multisig seed is empty"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet *wallet;
|
||||||
|
wallet = m_walletManager->restoreMultisigWallet(path, password, constants::networkType, multisigSeed, mmsRecovery, restoreHeight, constants::kdfRounds, subaddressLookahead);
|
||||||
|
|
||||||
|
m_openingWallet = true;
|
||||||
|
this->onWalletOpened(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::onWalletCreated(Wallet *wallet) {
|
void WindowManager::onWalletCreated(Wallet *wallet) {
|
||||||
// Currently only called when a wallet is created from device.
|
// Currently only called when a wallet is created from device.
|
||||||
auto state = wallet->status();
|
auto state = wallet->status();
|
||||||
|
@ -686,6 +749,8 @@ WalletWizard* WindowManager::createWizard(WalletWizard::Page startPage) {
|
||||||
connect(wizard, &WalletWizard::createWallet, this, &WindowManager::tryCreateWallet);
|
connect(wizard, &WalletWizard::createWallet, this, &WindowManager::tryCreateWallet);
|
||||||
connect(wizard, &WalletWizard::createWalletFromKeys, this, &WindowManager::tryCreateWalletFromKeys);
|
connect(wizard, &WalletWizard::createWalletFromKeys, this, &WindowManager::tryCreateWalletFromKeys);
|
||||||
connect(wizard, &WalletWizard::createWalletFromDevice, this, &WindowManager::tryCreateWalletFromDevice);
|
connect(wizard, &WalletWizard::createWalletFromDevice, this, &WindowManager::tryCreateWalletFromDevice);
|
||||||
|
connect(wizard, &WalletWizard::restoreMultisigWallet, this, &WindowManager::tryRestoreMultisigWallet);
|
||||||
|
connect(wizard, &WalletWizard::showWallet, [this](Wallet *wallet){this->onWalletOpened(wallet);});
|
||||||
return wizard;
|
return wizard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,12 +763,17 @@ void WindowManager::initWizard() {
|
||||||
this->showWizard(startPage);
|
this->showWizard(startPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::showWizard(WalletWizard::Page startPage) {
|
void WindowManager::showWizard(WalletWizard::Page startPage, Wallet *wallet) {
|
||||||
if (!m_wizard) {
|
if (!m_wizard) {
|
||||||
m_wizard = this->createWizard(startPage);
|
m_wizard = this->createWizard(startPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_wizard->resetFields();
|
m_wizard->resetFields();
|
||||||
|
|
||||||
|
if (wallet) {
|
||||||
|
m_wizard->setWallet(wallet);
|
||||||
|
}
|
||||||
|
|
||||||
m_wizard->setStartId(startPage);
|
m_wizard->setStartId(startPage);
|
||||||
m_wizard->restart();
|
m_wizard->restart();
|
||||||
m_wizard->setEnabled(true);
|
m_wizard->setEnabled(true);
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
void wizardOpenWallet();
|
void wizardOpenWallet();
|
||||||
void close();
|
void close();
|
||||||
void closeWindow(MainWindow *window);
|
void closeWindow(MainWindow *window);
|
||||||
void showWizard(WalletWizard::Page startPage);
|
void showWizard(WalletWizard::Page startPage, Wallet *wallet = nullptr);
|
||||||
void restartApplication(const QString &binaryFilename);
|
void restartApplication(const QString &binaryFilename);
|
||||||
void raise();
|
void raise();
|
||||||
|
|
||||||
|
@ -68,9 +68,10 @@ private slots:
|
||||||
void onChangeTheme(const QString &themeName);
|
void onChangeTheme(const QString &themeName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void tryCreateWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage, const QString &seedOffset, const QString &subaddressLookahead, bool newWallet);
|
void tryCreateWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage, const QString &seedOffset, const QString &subaddressLookahead, bool newWallet, bool giveToWizard = false);
|
||||||
void tryCreateWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight, const QString &subaddressLookahead);
|
void tryCreateWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight, const QString &subaddressLookahead);
|
||||||
void tryCreateWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, const QString &subaddressLookahead);
|
void tryCreateWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, const QString &subaddressLookahead);
|
||||||
|
void tryRestoreMultisigWallet(const QString &path, const QString &password, const QString &multisigSeed, const QString &mmsRecovery, quint64 restoreHeight, const QString &subaddressLookahead);
|
||||||
|
|
||||||
bool autoOpenWallet();
|
bool autoOpenWallet();
|
||||||
|
|
||||||
|
@ -107,6 +108,7 @@ private:
|
||||||
bool m_openWalletTriedOnce = false;
|
bool m_openWalletTriedOnce = false;
|
||||||
bool m_openingWallet = false;
|
bool m_openingWallet = false;
|
||||||
bool m_initialNetworkConfigured = false;
|
bool m_initialNetworkConfigured = false;
|
||||||
|
bool m_aboutToShowWizard = false;
|
||||||
|
|
||||||
QThread *m_cleanupThread;
|
QThread *m_cleanupThread;
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
<file>assets/images/eye_blind.png</file>
|
<file>assets/images/eye_blind.png</file>
|
||||||
<file>assets/images/file.png</file>
|
<file>assets/images/file.png</file>
|
||||||
<file>assets/images/file_manager_32px.png</file>
|
<file>assets/images/file_manager_32px.png</file>
|
||||||
|
<file>assets/images/freeze.png</file>
|
||||||
<file>assets/images/gnome-calc.png</file>
|
<file>assets/images/gnome-calc.png</file>
|
||||||
<file>assets/images/hd_32px.png</file>
|
<file>assets/images/hd_32px.png</file>
|
||||||
<file>assets/images/history.png</file>
|
<file>assets/images/history.png</file>
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
<file>assets/images/localMonero_logo.png</file>
|
<file>assets/images/localMonero_logo.png</file>
|
||||||
<file>assets/images/localMonero_register.svg</file>
|
<file>assets/images/localMonero_register.svg</file>
|
||||||
<file>assets/images/lock.svg</file>
|
<file>assets/images/lock.svg</file>
|
||||||
|
<file>assets/images/mail.png</file>
|
||||||
<file>assets/images/microphone.png</file>
|
<file>assets/images/microphone.png</file>
|
||||||
<file>assets/images/mining.png</file>
|
<file>assets/images/mining.png</file>
|
||||||
<file>assets/images/network.png</file>
|
<file>assets/images/network.png</file>
|
||||||
|
|
BIN
src/assets/images/freeze.png
Normal file
BIN
src/assets/images/freeze.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
src/assets/images/mail.png
Normal file
BIN
src/assets/images/mail.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -50,7 +50,7 @@ AccountSwitcherDialog::AccountSwitcherDialog(Wallet *wallet, QWidget *parent)
|
||||||
connect(m_wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection);
|
connect(m_wallet->subaddressAccount(), &SubaddressAccount::refreshFinished, this, &AccountSwitcherDialog::updateSelection);
|
||||||
|
|
||||||
this->update();
|
this->update();
|
||||||
this->updateSelection();
|
// this->updateSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSwitcherDialog::update() {
|
void AccountSwitcherDialog::update() {
|
||||||
|
|
|
@ -21,28 +21,35 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
|
||||||
m_wallet->setSeedLanguage(constants::seedLanguage);
|
m_wallet->setSeedLanguage(constants::seedLanguage);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString seedOffset = m_wallet->getCacheAttribute("feather.seedoffset");
|
if (m_wallet->isMultisig()) {
|
||||||
QString seed = m_wallet->getCacheAttribute("feather.seed");
|
this->setMultisigSeed(m_wallet->getMultisigSeed());
|
||||||
auto seedLength = m_wallet->seedLength();
|
ui->frameSeedOffset->setVisible(false);
|
||||||
|
|
||||||
QString seed_25_words = m_wallet->getSeed(seedOffset);
|
|
||||||
|
|
||||||
if (seedLength >= 24) {
|
|
||||||
ui->check_toggleSeedType->hide();
|
ui->check_toggleSeedType->hide();
|
||||||
this->setSeed(seed_25_words);
|
|
||||||
} else {
|
|
||||||
this->setSeed(seed);
|
|
||||||
ui->frameRestoreHeight->setVisible(false);
|
ui->frameRestoreHeight->setVisible(false);
|
||||||
|
} else {
|
||||||
|
QString seedOffset = m_wallet->getCacheAttribute("feather.seedoffset");
|
||||||
|
QString seed = m_wallet->getCacheAttribute("feather.seed");
|
||||||
|
auto seedLength = m_wallet->seedLength();
|
||||||
|
|
||||||
|
QString seed_25_words = m_wallet->getSeed(seedOffset);
|
||||||
|
|
||||||
|
if (seedLength >= 24) {
|
||||||
|
ui->check_toggleSeedType->hide();
|
||||||
|
this->setSeed(seed_25_words);
|
||||||
|
} else {
|
||||||
|
this->setSeed(seed);
|
||||||
|
ui->frameRestoreHeight->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->frameSeedOffset->setVisible(!seedOffset.isEmpty());
|
||||||
|
ui->line_seedOffset->setText(seedOffset);
|
||||||
|
|
||||||
|
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed](bool toggled){
|
||||||
|
this->setSeed(toggled ? seed_25_words : seed);
|
||||||
|
ui->frameRestoreHeight->setVisible(toggled);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->frameSeedOffset->setVisible(!seedOffset.isEmpty());
|
|
||||||
ui->line_seedOffset->setText(seedOffset);
|
|
||||||
|
|
||||||
connect(ui->check_toggleSeedType, &QCheckBox::toggled, [this, seed_25_words, seed](bool toggled){
|
|
||||||
this->setSeed(toggled ? seed_25_words : seed);
|
|
||||||
ui->frameRestoreHeight->setVisible(toggled);
|
|
||||||
});
|
|
||||||
|
|
||||||
ui->label_restoreHeightHelp->setHelpText("", "Should you restore your wallet in the future, "
|
ui->label_restoreHeightHelp->setHelpText("", "Should you restore your wallet in the future, "
|
||||||
"specifying this block number will recover your wallet quicker.", "restore_height");
|
"specifying this block number will recover your wallet quicker.", "restore_height");
|
||||||
|
|
||||||
|
@ -59,10 +66,23 @@ void SeedDialog::setSeed(const QString &seed) {
|
||||||
"</p>"
|
"</p>"
|
||||||
"<b>WARNING:</b>"
|
"<b>WARNING:</b>"
|
||||||
"<ul>"
|
"<ul>"
|
||||||
"<li>Never disclose your seed.</li>"
|
"<li>Never disclose your seed</li>"
|
||||||
"<li>Never type it on a website</li>"
|
"<li>Never type it on a website</li>"
|
||||||
"<li>Do not store it electronically</li>"
|
"<li>Do not store it electronically</li>"
|
||||||
"</ul>").arg(words));
|
"</ul>").arg(words));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SeedDialog::setMultisigSeed(const QString &seed) {
|
||||||
|
ui->seed->setPlainText(seed);
|
||||||
|
|
||||||
|
ui->label_warning->setText("<p>This seed will allow you to recover your wallet in case "
|
||||||
|
"of computer failure."
|
||||||
|
"</p>"
|
||||||
|
"<b>WARNING:</b>"
|
||||||
|
"<ul>"
|
||||||
|
"<li>Never disclose your seed</li>"
|
||||||
|
"<li>Never type it on a website</li>"
|
||||||
|
"</ul>");
|
||||||
|
}
|
||||||
|
|
||||||
SeedDialog::~SeedDialog() = default;
|
SeedDialog::~SeedDialog() = default;
|
|
@ -23,6 +23,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setSeed(const QString &seed);
|
void setSeed(const QString &seed);
|
||||||
|
void setMultisigSeed(const QString &seed);
|
||||||
|
|
||||||
QScopedPointer<Ui::SeedDialog> ui;
|
QScopedPointer<Ui::SeedDialog> ui;
|
||||||
Wallet *m_wallet;
|
Wallet *m_wallet;
|
||||||
|
|
|
@ -103,6 +103,38 @@ void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
|
||||||
this->setupConstructionData(ci);
|
this->setupConstructionData(ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TxConfAdvDialog::setMultisigTransaction(PendingTransaction *tx) {
|
||||||
|
m_multisig = true;
|
||||||
|
m_tx = tx;
|
||||||
|
m_tx->refresh();
|
||||||
|
|
||||||
|
ui->btn_exportSigned->hide();
|
||||||
|
ui->btn_exportTxKey->hide();
|
||||||
|
ui->btn_sign->show();
|
||||||
|
ui->btn_send->show();
|
||||||
|
ui->btn_send->setEnabled(false);
|
||||||
|
|
||||||
|
if (m_tx->haveWeSigned()) {
|
||||||
|
ui->btn_sign->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_tx->enoughMultisigSignatures()) {
|
||||||
|
ui->btn_send->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ui->btn_send->setText(m_tx->signaturesNeeded() == 1 ? "S");
|
||||||
|
//
|
||||||
|
// if (m_tx->signaturesNeeded() == 1) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
PendingTransactionInfo *ptx = m_tx->transaction(0);
|
||||||
|
|
||||||
|
ui->txid->setText("n/a");
|
||||||
|
this->setAmounts(tx->amount(), tx->fee());
|
||||||
|
this->setupConstructionData(ptx);
|
||||||
|
}
|
||||||
|
|
||||||
void TxConfAdvDialog::setAmounts(quint64 amount, quint64 fee) {
|
void TxConfAdvDialog::setAmounts(quint64 amount, quint64 fee) {
|
||||||
QString preferredCur = conf()->get(Config::preferredFiatCurrency).toString();
|
QString preferredCur = conf()->get(Config::preferredFiatCurrency).toString();
|
||||||
|
|
||||||
|
@ -180,6 +212,29 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TxConfAdvDialog::signTransaction() {
|
void TxConfAdvDialog::signTransaction() {
|
||||||
|
if (m_multisig) {
|
||||||
|
|
||||||
|
m_tx->signMultisigTx();
|
||||||
|
if (m_tx->status() != PendingTransaction::Status_Ok) {
|
||||||
|
Utils::showError(this, "Unable to sign multisig transaction", m_tx->errorString());
|
||||||
|
|
||||||
|
// auto button = QMessageBox::question(this, "Export multisig info?", "Do you want to export multisig info now?");
|
||||||
|
// if (result == QMessageBox::Yes) {
|
||||||
|
// m_wallet->mmsStore()->ex
|
||||||
|
// }
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_tx->enoughMultisigSignatures()) {
|
||||||
|
ui->btn_send->setText("Broadcast");
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->btn_sign->setEnabled(false);
|
||||||
|
ui->btn_send->setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this->accept();
|
this->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ public:
|
||||||
|
|
||||||
void setTransaction(PendingTransaction *tx, bool isSigned = true); // #TODO: have libwallet return a UnsignedTransaction, this is just dumb
|
void setTransaction(PendingTransaction *tx, bool isSigned = true); // #TODO: have libwallet return a UnsignedTransaction, this is just dumb
|
||||||
void setUnsignedTransaction(UnsignedTransaction *utx);
|
void setUnsignedTransaction(UnsignedTransaction *utx);
|
||||||
|
void setMultisigTransaction(PendingTransaction *tx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupConstructionData(ConstructionInfo *ci);
|
void setupConstructionData(ConstructionInfo *ci);
|
||||||
|
@ -51,6 +52,7 @@ private:
|
||||||
QMenu *m_exportTxKeyMenu;
|
QMenu *m_exportTxKeyMenu;
|
||||||
QString m_txid;
|
QString m_txid;
|
||||||
bool m_offline;
|
bool m_offline;
|
||||||
|
bool m_multisig = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_TXCONFADVDIALOG_H
|
#endif //FEATHER_TXCONFADVDIALOG_H
|
||||||
|
|
|
@ -76,13 +76,27 @@ TxConfDialog::TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString
|
||||||
ui->label_fee->setToolTip("Unrealistic fee. You may be connected to a malicious node.");
|
ui->label_fee->setToolTip("Unrealistic fee. You may be connected to a malicious node.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Send");
|
bool readyToSend = tx->enoughMultisigSignatures();
|
||||||
|
|
||||||
|
if (!readyToSend) {
|
||||||
|
ui->combo_sendTo->addItem("All cosigners");
|
||||||
|
ui->combo_sendTo->addItems(m_wallet->getMultisigSigners());
|
||||||
|
} else {
|
||||||
|
ui->label_sendTo->hide();
|
||||||
|
ui->combo_sendTo->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->buttonBox->button(QDialogButtonBox::Ok)->setText(!readyToSend ? "Sign" : "Send");
|
||||||
|
|
||||||
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
connect(ui->btn_Advanced, &QPushButton::clicked, this, &TxConfDialog::setShowAdvanced);
|
||||||
|
|
||||||
this->adjustSize();
|
this->adjustSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quint32 TxConfDialog::getMultisigSignerIndex() {
|
||||||
|
return ui->combo_sendTo->currentIndex();
|
||||||
|
}
|
||||||
|
|
||||||
void TxConfDialog::setShowAdvanced() {
|
void TxConfDialog::setShowAdvanced() {
|
||||||
this->showAdvanced = true;
|
this->showAdvanced = true;
|
||||||
QDialog::reject();
|
QDialog::reject();
|
||||||
|
|
|
@ -23,6 +23,8 @@ public:
|
||||||
explicit TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr);
|
explicit TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr);
|
||||||
~TxConfDialog() override;
|
~TxConfDialog() override;
|
||||||
|
|
||||||
|
quint32 getMultisigSignerIndex();
|
||||||
|
|
||||||
bool showAdvanced = false;
|
bool showAdvanced = false;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -119,6 +119,16 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_sendTo">
|
||||||
|
<property name="text">
|
||||||
|
<string>Send to:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QComboBox" name="combo_sendTo"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -76,6 +76,23 @@ void Coins::refresh()
|
||||||
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
|
ci->m_coinbase = td.m_tx.vin.size() == 1 && td.m_tx.vin[0].type() == typeid(cryptonote::txin_gen);
|
||||||
ci->m_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey));
|
ci->m_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey));
|
||||||
ci->m_change = m_wallet2->is_change(td);
|
ci->m_change = m_wallet2->is_change(td);
|
||||||
|
ci->m_keyImagePartial = td.m_key_image_partial;
|
||||||
|
|
||||||
|
bool haveMultisigK = true;
|
||||||
|
if (td.m_multisig_k.empty()) {
|
||||||
|
haveMultisigK = false;
|
||||||
|
}
|
||||||
|
for (const auto &k : td.m_multisig_k) {
|
||||||
|
if (k == rct::zero()) {
|
||||||
|
haveMultisigK = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci->m_haveMultisigK = haveMultisigK;
|
||||||
|
|
||||||
|
for (const auto& info : td.m_multisig_info) {
|
||||||
|
ci->m_multisigInfo.append(QString::fromStdString(m_wallet2->get_signer_label(info.m_signer)));
|
||||||
|
}
|
||||||
|
|
||||||
m_rows.push_back(ci);
|
m_rows.push_back(ci);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
bool coin(int index, std::function<void (CoinsInfo &)> callback);
|
bool coin(int index, std::function<void (CoinsInfo &)> callback);
|
||||||
CoinsInfo * coin(int index);
|
CoinsInfo * coin(int index);
|
||||||
void refresh();
|
|
||||||
void refreshUnlocked();
|
void refreshUnlocked();
|
||||||
void freeze(QString &publicKey);
|
void freeze(QString &publicKey);
|
||||||
void thaw(QString &publicKey);
|
void thaw(QString &publicKey);
|
||||||
|
@ -41,6 +40,9 @@ public:
|
||||||
quint64 count() const;
|
quint64 count() const;
|
||||||
void clearRows();
|
void clearRows();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void refresh();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void refreshStarted() const;
|
void refreshStarted() const;
|
||||||
void refreshFinished() const;
|
void refreshFinished() const;
|
||||||
|
|
1025
src/libwalletqt/MultisigMessageStore.cpp
Normal file
1025
src/libwalletqt/MultisigMessageStore.cpp
Normal file
File diff suppressed because it is too large
Load diff
144
src/libwalletqt/MultisigMessageStore.h
Normal file
144
src/libwalletqt/MultisigMessageStore.h
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_MULTISIGMESSAGESTORE_H
|
||||||
|
#define FEATHER_MULTISIGMESSAGESTORE_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QList>
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "Wallet.h"
|
||||||
|
|
||||||
|
#include "wallet/message_store.h"
|
||||||
|
#include "cryptonote_basic/blobdatatype.h"
|
||||||
|
|
||||||
|
namespace tools {
|
||||||
|
class wallet2;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultisigMessage;
|
||||||
|
class TxProposal;
|
||||||
|
|
||||||
|
class MultisigMessageStore : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct SignerInfo {
|
||||||
|
QString label;
|
||||||
|
QString publicKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SetupMode {
|
||||||
|
AUTOMATIC = 0,
|
||||||
|
SEMI_AUTOMATIC,
|
||||||
|
MANUAL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetupKey {
|
||||||
|
quint32 threshold;
|
||||||
|
quint32 participants;
|
||||||
|
QString service;
|
||||||
|
SetupMode mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool txProposal(int index, std::function<void (TxProposal &)> callback);
|
||||||
|
quint64 txProposalCount() const;
|
||||||
|
|
||||||
|
|
||||||
|
bool message(int index, std::function<void (MultisigMessage &)> callback);
|
||||||
|
|
||||||
|
void receiveMessages();
|
||||||
|
|
||||||
|
MultisigMessage * message(int index);
|
||||||
|
void refresh(bool next = true);
|
||||||
|
|
||||||
|
void next(bool forceSync = false, bool calledFromRefresh = false);
|
||||||
|
|
||||||
|
bool signTx(quint32 id);
|
||||||
|
bool deleteMessage(quint32 id);
|
||||||
|
void deleteAllMessages();
|
||||||
|
|
||||||
|
std::string exportMessage(quint32 id);
|
||||||
|
|
||||||
|
void sendReadyMessages();
|
||||||
|
|
||||||
|
QString errorString();
|
||||||
|
|
||||||
|
void setServiceDetails(const QString &serviceUrl, const QString &serviceLogin);
|
||||||
|
|
||||||
|
bool registerChannel(QString &channel, quint32 user_limit);
|
||||||
|
|
||||||
|
bool prepareMultisig();
|
||||||
|
bool makeMultisig(quint32 threshold, const std::vector<std::string> &kexMessages);
|
||||||
|
bool exchangeMultisig(const std::vector<std::string> &kexMessages);
|
||||||
|
bool exportMultisig();
|
||||||
|
bool importMultisig(const std::vector<cryptonote::blobdata> info);
|
||||||
|
bool signMultisigTx(const cryptonote::blobdata &data);
|
||||||
|
bool submitMultisigTx(const cryptonote::blobdata &data);
|
||||||
|
|
||||||
|
quint64 count() const;
|
||||||
|
void clearRows();
|
||||||
|
|
||||||
|
bool havePartiallySignedTxWaiting();
|
||||||
|
|
||||||
|
SignerInfo getSignerInfo(quint32 index);
|
||||||
|
|
||||||
|
bool processSyncData();
|
||||||
|
bool sendPendingTransaction(quint32 message_id, quint32 cosigner);
|
||||||
|
|
||||||
|
QString createSetupKey(quint32 threshold, quint32 signers, const QString &service, const QString &channel, SetupMode mode);
|
||||||
|
bool checkSetupKey(const QString &setupKey, SetupKey &key);
|
||||||
|
|
||||||
|
void init(const QString &setupKey, const QString &ownLabel);
|
||||||
|
void setSigner(quint32 index, const QString& label, const QString& address);
|
||||||
|
|
||||||
|
QVector<SignerInfo> getSignerInfo();
|
||||||
|
|
||||||
|
QString getRecoveryInfo();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void refreshStarted() const;
|
||||||
|
void refreshFinished() const;
|
||||||
|
void multisigWalletCreated(const QString &address);
|
||||||
|
void signersUpdated() const;
|
||||||
|
|
||||||
|
void askToSign(PendingTransaction *tx) const;
|
||||||
|
void statusChanged(const QString &status, bool finished);
|
||||||
|
|
||||||
|
void multisigInfoExported();
|
||||||
|
void multisigInfoImported();
|
||||||
|
|
||||||
|
void connectionError();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit MultisigMessageStore(Wallet *wallet, tools::wallet2 *wallet2, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void setErrorString(const QString &errorString);
|
||||||
|
void clearStatus();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Wallet;
|
||||||
|
|
||||||
|
Wallet *m_wallet;
|
||||||
|
tools::wallet2 *m_wallet2;
|
||||||
|
QList<MultisigMessage*> m_rows;
|
||||||
|
|
||||||
|
QList<TxProposal*> m_txProposals;
|
||||||
|
QHash<QString, quint64> m_txProposalsIndex;
|
||||||
|
|
||||||
|
// QList<TxProposal*> m_txProposals;
|
||||||
|
QReadWriteLock lock{QReadWriteLock::RecursionMode::Recursive};
|
||||||
|
|
||||||
|
quint32 m_sendToIndex = 1;
|
||||||
|
QString m_status;
|
||||||
|
QString m_errorString;
|
||||||
|
|
||||||
|
mutable QReadWriteLock m_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_MULTISIGMESSAGESTORE_H
|
|
@ -53,6 +53,14 @@ QStringList PendingTransaction::txid() const
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList PendingTransaction::prefixHashes() const
|
||||||
|
{
|
||||||
|
QStringList prefixHashes;
|
||||||
|
for (const auto &hash : m_pimpl->prefixHashes()) {
|
||||||
|
prefixHashes.append(QString::fromStdString(hash));
|
||||||
|
}
|
||||||
|
return prefixHashes;
|
||||||
|
}
|
||||||
|
|
||||||
quint64 PendingTransaction::txCount() const
|
quint64 PendingTransaction::txCount() const
|
||||||
{
|
{
|
||||||
|
@ -98,6 +106,48 @@ void PendingTransaction::refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quint32 PendingTransaction::saveToMMS() {
|
||||||
|
return m_pimpl->saveToMMS();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PendingTransaction::destinationAddresses(int index) {
|
||||||
|
std::vector<std::string> dests = m_pimpl->destinations(index);
|
||||||
|
QStringList destinations;
|
||||||
|
for (const auto &dest : dests) {
|
||||||
|
destinations << QString::fromStdString(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingTransaction::signMultisigTx() {
|
||||||
|
m_pimpl->signMultisigTx();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 PendingTransaction::signaturesNeeded() {
|
||||||
|
return m_pimpl->signaturesNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingTransaction::enoughMultisigSignatures() {
|
||||||
|
return m_pimpl->enoughMultisigSignatures();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PendingTransaction::signersKeys() {
|
||||||
|
QStringList keys;
|
||||||
|
for (const auto &key : m_pimpl->signersKeys()) {
|
||||||
|
keys.append(QString::fromStdString(key));
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingTransaction::haveWeSigned() const {
|
||||||
|
return m_pimpl->haveWeSigned();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PendingTransaction::canSign() const {
|
||||||
|
return m_pimpl->canSign();
|
||||||
|
}
|
||||||
|
|
||||||
PendingTransaction::PendingTransaction(Monero::PendingTransaction *pt, QObject *parent)
|
PendingTransaction::PendingTransaction(Monero::PendingTransaction *pt, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_pimpl(pt)
|
, m_pimpl(pt)
|
||||||
|
|
|
@ -31,12 +31,23 @@ public:
|
||||||
quint64 dust() const;
|
quint64 dust() const;
|
||||||
quint64 fee() const;
|
quint64 fee() const;
|
||||||
QStringList txid() const;
|
QStringList txid() const;
|
||||||
|
QStringList prefixHashes() const;
|
||||||
quint64 txCount() const;
|
quint64 txCount() const;
|
||||||
QList<QVariant> subaddrIndices() const;
|
QList<QVariant> subaddrIndices() const;
|
||||||
std::string unsignedTxToBin() const;
|
std::string unsignedTxToBin() const;
|
||||||
QString unsignedTxToBase64() const;
|
QString unsignedTxToBase64() const;
|
||||||
QString signedTxToHex(int index) const;
|
QString signedTxToHex(int index) const;
|
||||||
void refresh();
|
void refresh();
|
||||||
|
quint32 saveToMMS();
|
||||||
|
QStringList destinationAddresses(int index);
|
||||||
|
void signMultisigTx();
|
||||||
|
quint64 signaturesNeeded();
|
||||||
|
bool enoughMultisigSignatures();
|
||||||
|
|
||||||
|
QStringList signersKeys();
|
||||||
|
|
||||||
|
bool haveWeSigned() const;
|
||||||
|
bool canSign() const;
|
||||||
|
|
||||||
PendingTransactionInfo * transaction(int index) const;
|
PendingTransactionInfo * transaction(int index) const;
|
||||||
|
|
||||||
|
|
|
@ -185,6 +185,10 @@ void TransactionHistory::refresh()
|
||||||
t->m_rings.append(ring);
|
t->m_rings.append(ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qDebug() << pd.m_tx.extra;
|
||||||
|
qDebug() << pd.m_tx.version;
|
||||||
|
t->m_prefixHash = QString::fromStdString(epee::string_tools::pod_to_hex(cryptonote::get_transaction_prefix_hash(pd.m_tx)));
|
||||||
|
|
||||||
m_rows.append(t);
|
m_rows.append(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "AddressBook.h"
|
#include "AddressBook.h"
|
||||||
#include "Coins.h"
|
#include "Coins.h"
|
||||||
|
#include "MultisigMessageStore.h"
|
||||||
#include "Subaddress.h"
|
#include "Subaddress.h"
|
||||||
#include "SubaddressAccount.h"
|
#include "SubaddressAccount.h"
|
||||||
#include "TransactionHistory.h"
|
#include "TransactionHistory.h"
|
||||||
|
@ -22,6 +23,8 @@
|
||||||
#include "model/SubaddressModel.h"
|
#include "model/SubaddressModel.h"
|
||||||
#include "model/SubaddressAccountModel.h"
|
#include "model/SubaddressAccountModel.h"
|
||||||
#include "model/CoinsModel.h"
|
#include "model/CoinsModel.h"
|
||||||
|
#include "model/MultisigMessageModel.h"
|
||||||
|
#include "model/MultisigIncomingTxModel.h"
|
||||||
|
|
||||||
#include "utils/ScopeGuard.h"
|
#include "utils/ScopeGuard.h"
|
||||||
|
|
||||||
|
@ -47,9 +50,11 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
|
||||||
, m_subaddressAccount(new SubaddressAccount(this, wallet->getWallet(), this))
|
, m_subaddressAccount(new SubaddressAccount(this, wallet->getWallet(), this))
|
||||||
, m_refreshNow(false)
|
, m_refreshNow(false)
|
||||||
, m_refreshEnabled(false)
|
, m_refreshEnabled(false)
|
||||||
|
, m_mmsRefreshEnabled(false)
|
||||||
, m_scheduler(this)
|
, m_scheduler(this)
|
||||||
, m_useSSL(true)
|
, m_useSSL(true)
|
||||||
, m_coins(new Coins(this, wallet->getWallet(), this))
|
, m_coins(new Coins(this, wallet->getWallet(), this))
|
||||||
|
, m_mmsStore(new MultisigMessageStore(this, wallet->getWallet(), this))
|
||||||
, m_storeTimer(new QTimer(this))
|
, m_storeTimer(new QTimer(this))
|
||||||
{
|
{
|
||||||
m_walletListener = new WalletListenerImpl(this);
|
m_walletListener = new WalletListenerImpl(this);
|
||||||
|
@ -60,6 +65,8 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
|
||||||
m_subaddressModel = new SubaddressModel(this, m_subaddress);
|
m_subaddressModel = new SubaddressModel(this, m_subaddress);
|
||||||
m_subaddressAccountModel = new SubaddressAccountModel(this, m_subaddressAccount);
|
m_subaddressAccountModel = new SubaddressAccountModel(this, m_subaddressAccount);
|
||||||
m_coinsModel = new CoinsModel(this, m_coins);
|
m_coinsModel = new CoinsModel(this, m_coins);
|
||||||
|
m_mmsModel = new MultisigMessageModel(this, m_mmsStore);
|
||||||
|
m_msIncomingTxModel = new MultisigIncomingTxModel(this, m_mmsStore);
|
||||||
|
|
||||||
if (this->status() == Status_Ok) {
|
if (this->status() == Status_Ok) {
|
||||||
startRefreshThread();
|
startRefreshThread();
|
||||||
|
@ -86,6 +93,9 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
|
||||||
connect(m_subaddress, &Subaddress::corrupted, [this]{
|
connect(m_subaddress, &Subaddress::corrupted, [this]{
|
||||||
emit keysCorrupted();
|
emit keysCorrupted();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(m_mmsStore, &MultisigMessageStore::multisigInfoImported, m_coins, &Coins::refresh);
|
||||||
|
connect(m_mmsStore, &MultisigMessageStore::multisigInfoExported, m_coins, &Coins::refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #################### Status ####################
|
// #################### Status ####################
|
||||||
|
@ -128,6 +138,12 @@ bool Wallet::viewOnly() const {
|
||||||
return m_wallet2->watch_only();
|
return m_wallet2->watch_only();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wallet::isMultisig() const {
|
||||||
|
bool ready, multisig;
|
||||||
|
multisig = m_wallet2->multisig(&ready);
|
||||||
|
return multisig;
|
||||||
|
}
|
||||||
|
|
||||||
bool Wallet::isDeterministic() const {
|
bool Wallet::isDeterministic() const {
|
||||||
return m_wallet2->is_deterministic();
|
return m_wallet2->is_deterministic();
|
||||||
}
|
}
|
||||||
|
@ -342,6 +358,12 @@ QString Wallet::getSeed(const QString &seedOffset) const {
|
||||||
return QString::fromStdString(std::string(seed.data(), seed.size()));
|
return QString::fromStdString(std::string(seed.data(), seed.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Wallet::getMultisigSeed() const {
|
||||||
|
epee::wipeable_string seed;
|
||||||
|
m_wallet2->get_multisig_seed(seed, "");
|
||||||
|
return QString::fromStdString(std::string(seed.data(), seed.size()));
|
||||||
|
}
|
||||||
|
|
||||||
qsizetype Wallet::seedLength() const {
|
qsizetype Wallet::seedLength() const {
|
||||||
auto seedLength = this->getCacheAttribute("feather.seed").split(" ", Qt::SkipEmptyParts).length();
|
auto seedLength = this->getCacheAttribute("feather.seed").split(" ", Qt::SkipEmptyParts).length();
|
||||||
return seedLength ? seedLength : 25;
|
return seedLength ? seedLength : 25;
|
||||||
|
@ -420,47 +442,54 @@ void Wallet::startRefreshThread()
|
||||||
const auto future = m_scheduler.run([this] {
|
const auto future = m_scheduler.run([this] {
|
||||||
// Beware! This code does not run in the GUI thread.
|
// Beware! This code does not run in the GUI thread.
|
||||||
|
|
||||||
constexpr const std::chrono::seconds refreshInterval{10};
|
|
||||||
constexpr const std::chrono::milliseconds intervalResolution{100};
|
constexpr const std::chrono::milliseconds intervalResolution{100};
|
||||||
|
|
||||||
auto last = std::chrono::steady_clock::now();
|
auto last = std::chrono::steady_clock::now();
|
||||||
while (!m_scheduler.stopping())
|
while (!m_scheduler.stopping())
|
||||||
{
|
{
|
||||||
if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected()))
|
if ((!isHwBacked() || isDeviceConnected()))
|
||||||
{
|
{
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
const auto elapsed = now - last;
|
const auto elapsed = now - last;
|
||||||
if (elapsed >= refreshInterval || m_refreshNow)
|
if (elapsed >= m_refreshInterval || m_refreshNow)
|
||||||
{
|
{
|
||||||
m_refreshNow = false;
|
if (m_refreshEnabled) {
|
||||||
|
m_refreshNow = false;
|
||||||
|
|
||||||
// get daemonHeight and targetHeight
|
// get daemonHeight and targetHeight
|
||||||
// daemonHeight and targetHeight will be 0 if call to get_info fails
|
// daemonHeight and targetHeight will be 0 if call to get_info fails
|
||||||
quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight();
|
quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight();
|
||||||
bool success = daemonHeight > 0;
|
bool success = daemonHeight > 0;
|
||||||
|
|
||||||
quint64 targetHeight = 0;
|
quint64 targetHeight = 0;
|
||||||
if (success) {
|
if (success) {
|
||||||
targetHeight = m_walletImpl->daemonBlockChainTargetHeight();
|
targetHeight = m_walletImpl->daemonBlockChainTargetHeight();
|
||||||
}
|
|
||||||
bool haveHeights = (daemonHeight > 0 && targetHeight > 0);
|
|
||||||
|
|
||||||
emit heightsRefreshed(haveHeights, daemonHeight, targetHeight);
|
|
||||||
|
|
||||||
// Don't call refresh function if we don't have the daemon and target height
|
|
||||||
// We do this to prevent to UI from getting confused about the amount of blocks that are still remaining
|
|
||||||
if (haveHeights) {
|
|
||||||
QMutexLocker locker(&m_asyncMutex);
|
|
||||||
|
|
||||||
if (m_newWallet) {
|
|
||||||
// Set blockheight to daemonHeight for newly created wallets to speed up initial sync
|
|
||||||
m_walletImpl->setRefreshFromBlockHeight(daemonHeight);
|
|
||||||
m_newWallet = false;
|
|
||||||
}
|
}
|
||||||
|
bool haveHeights = (daemonHeight > 0 && targetHeight > 0);
|
||||||
|
|
||||||
m_walletImpl->refresh();
|
emit heightsRefreshed(haveHeights, daemonHeight, targetHeight);
|
||||||
|
|
||||||
|
// Don't call refresh function if we don't have the daemon and target height
|
||||||
|
// We do this to prevent to UI from getting confused about the amount of blocks that are still remaining
|
||||||
|
if (haveHeights) {
|
||||||
|
QMutexLocker locker(&m_asyncMutex);
|
||||||
|
|
||||||
|
if (m_newWallet) {
|
||||||
|
// Set blockheight to daemonHeight for newly created wallets to speed up initial sync
|
||||||
|
m_walletImpl->setRefreshFromBlockHeight(daemonHeight);
|
||||||
|
m_newWallet = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_walletImpl->refresh();
|
||||||
|
}
|
||||||
|
last = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_mmsRefreshEnabled)
|
||||||
|
{
|
||||||
|
m_mmsStore->receiveMessages();
|
||||||
|
last = std::chrono::steady_clock::now();
|
||||||
}
|
}
|
||||||
last = std::chrono::steady_clock::now();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,6 +568,7 @@ void Wallet::onNewBlock(uint64_t walletHeight) {
|
||||||
void Wallet::onUpdated() {
|
void Wallet::onUpdated() {
|
||||||
this->updateBalance();
|
this->updateBalance();
|
||||||
if (this->isSynchronized()) {
|
if (this->isSynchronized()) {
|
||||||
|
// m_mmsStore->exportMultisig();
|
||||||
this->refreshModels();
|
this->refreshModels();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,6 +593,11 @@ void Wallet::refreshModels() {
|
||||||
m_history->refresh();
|
m_history->refresh();
|
||||||
m_coins->refresh();
|
m_coins->refresh();
|
||||||
this->subaddress()->refresh(this->currentSubaddressAccount());
|
this->subaddress()->refresh(this->currentSubaddressAccount());
|
||||||
|
m_mmsStore->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wallet::setRefreshInterval(qint64 seconds) {
|
||||||
|
m_refreshInterval = std::chrono::seconds{seconds};
|
||||||
}
|
}
|
||||||
|
|
||||||
// #################### Hardware wallet ####################
|
// #################### Hardware wallet ####################
|
||||||
|
@ -1023,6 +1058,13 @@ PendingTransaction * Wallet::loadSignedTxFromStr(const std::string &data)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PendingTransaction * Wallet::restoreMultisigTransaction(const std::string &data)
|
||||||
|
{
|
||||||
|
Monero::PendingTransaction *ptImpl = m_walletImpl->restoreMultisigTransaction(data);
|
||||||
|
PendingTransaction *result = new PendingTransaction(ptImpl, this);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool Wallet::submitTxFile(const QString &fileName) const
|
bool Wallet::submitTxFile(const QString &fileName) const
|
||||||
{
|
{
|
||||||
qDebug() << "Trying to submit " << fileName;
|
qDebug() << "Trying to submit " << fileName;
|
||||||
|
@ -1103,6 +1145,18 @@ CoinsModel* Wallet::coinsModel() const {
|
||||||
return m_coinsModel;
|
return m_coinsModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MultisigMessageStore* Wallet::mmsStore() const {
|
||||||
|
return m_mmsStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultisigMessageModel* Wallet::mmsModel() const {
|
||||||
|
return m_mmsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultisigIncomingTxModel* Wallet::msIncomingTxModel() const {
|
||||||
|
return m_msIncomingTxModel;
|
||||||
|
}
|
||||||
|
|
||||||
// #################### Transaction proofs ####################
|
// #################### Transaction proofs ####################
|
||||||
|
|
||||||
QString Wallet::getTxKey(const QString &txid) const {
|
QString Wallet::getTxKey(const QString &txid) const {
|
||||||
|
@ -1287,6 +1341,55 @@ QString Wallet::make_uri(const QString &address, quint64 &amount, const QString
|
||||||
return QString::fromStdString(uri);
|
return QString::fromStdString(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #################### Multisig ####################
|
||||||
|
|
||||||
|
void Wallet::setMMSRefreshEnabled(bool enabled) {
|
||||||
|
m_mmsRefreshEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Wallet::hasMultisigPartialKeyImages() {
|
||||||
|
return m_walletImpl->hasMultisigPartialKeyImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Wallet::getMultisigSigners() {
|
||||||
|
QStringList signers;
|
||||||
|
mms::message_store& ms = m_wallet2->get_message_store();
|
||||||
|
quint32 authorizedSigners = ms.get_num_authorized_signers();
|
||||||
|
for (uint32_t j = 1; j < authorizedSigners; ++j)
|
||||||
|
{
|
||||||
|
mms::authorized_signer signer = ms.get_signer(j);
|
||||||
|
signers << QString::fromStdString(ms.signer_to_string(signer, 50));;
|
||||||
|
}
|
||||||
|
return signers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Wallet::getMultisigSignerConfig() {
|
||||||
|
mms::message_store& ms = m_wallet2->get_message_store();
|
||||||
|
std::string signer_config;
|
||||||
|
ms.get_signer_config(signer_config);
|
||||||
|
std::string hex_signer_config = epee::string_tools::buff_to_hex_nodelimer(signer_config);
|
||||||
|
return QString::fromStdString(hex_signer_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 Wallet::multisigSigners() {
|
||||||
|
return m_wallet2->get_multisig_signers();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 Wallet::multisigThreshold() {
|
||||||
|
mms::message_store& ms = m_wallet2->get_message_store();
|
||||||
|
quint32 authorizedSigners = ms.get_num_required_signers();
|
||||||
|
return authorizedSigners;
|
||||||
|
}
|
||||||
|
|
||||||
|
//QString Wallet::originalPrimaryAddress() {
|
||||||
|
//// m_wallet2->m_original_address
|
||||||
|
// return "";
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//QString Wallet::originalSecretViewkey() {
|
||||||
|
// return QString::fromStdString(epee::string_tools::pod_to_hex(m_wallet2->m_original_view_secret_key));
|
||||||
|
//}
|
||||||
|
|
||||||
// #################### Misc / Unused ####################
|
// #################### Misc / Unused ####################
|
||||||
|
|
||||||
quint64 Wallet::getBytesReceived() const {
|
quint64 Wallet::getBytesReceived() const {
|
||||||
|
|
|
@ -71,6 +71,9 @@ class SubaddressAccount;
|
||||||
class SubaddressAccountModel;
|
class SubaddressAccountModel;
|
||||||
class Coins;
|
class Coins;
|
||||||
class CoinsModel;
|
class CoinsModel;
|
||||||
|
class MultisigMessageStore;
|
||||||
|
class MultisigMessageModel;
|
||||||
|
class MultisigIncomingTxModel;
|
||||||
|
|
||||||
struct TxProofResult {
|
struct TxProofResult {
|
||||||
TxProofResult() {}
|
TxProofResult() {}
|
||||||
|
@ -133,6 +136,8 @@ public:
|
||||||
//! returns if view only wallet
|
//! returns if view only wallet
|
||||||
bool viewOnly() const;
|
bool viewOnly() const;
|
||||||
|
|
||||||
|
bool isMultisig() const;
|
||||||
|
|
||||||
//! return true if deterministic keys
|
//! return true if deterministic keys
|
||||||
bool isDeterministic() const;
|
bool isDeterministic() const;
|
||||||
|
|
||||||
|
@ -175,6 +180,7 @@ public:
|
||||||
|
|
||||||
//! returns mnemonic seed
|
//! returns mnemonic seed
|
||||||
QString getSeed(const QString &seedOffset) const;
|
QString getSeed(const QString &seedOffset) const;
|
||||||
|
QString getMultisigSeed() const;
|
||||||
|
|
||||||
qsizetype seedLength() const;
|
qsizetype seedLength() const;
|
||||||
|
|
||||||
|
@ -227,6 +233,8 @@ public:
|
||||||
|
|
||||||
void refreshModels();
|
void refreshModels();
|
||||||
|
|
||||||
|
void setRefreshInterval(qint64 seconds);
|
||||||
|
|
||||||
// ##### Hardware wallet #####
|
// ##### Hardware wallet #####
|
||||||
bool isHwBacked() const;
|
bool isHwBacked() const;
|
||||||
bool isLedger() const;
|
bool isLedger() const;
|
||||||
|
@ -342,6 +350,8 @@ public:
|
||||||
PendingTransaction * loadSignedTxFile(const QString &fileName);
|
PendingTransaction * loadSignedTxFile(const QString &fileName);
|
||||||
PendingTransaction * loadSignedTxFromStr(const std::string &data);
|
PendingTransaction * loadSignedTxFromStr(const std::string &data);
|
||||||
|
|
||||||
|
PendingTransaction * restoreMultisigTransaction(const std::string &data);
|
||||||
|
|
||||||
//! Submit a transfer from file
|
//! Submit a transfer from file
|
||||||
bool submitTxFile(const QString &fileName) const;
|
bool submitTxFile(const QString &fileName) const;
|
||||||
|
|
||||||
|
@ -359,6 +369,9 @@ public:
|
||||||
SubaddressAccountModel* subaddressAccountModel() const;
|
SubaddressAccountModel* subaddressAccountModel() const;
|
||||||
Coins* coins() const;
|
Coins* coins() const;
|
||||||
CoinsModel* coinsModel() const;
|
CoinsModel* coinsModel() const;
|
||||||
|
MultisigMessageModel* mmsModel() const;
|
||||||
|
MultisigIncomingTxModel* msIncomingTxModel() const;
|
||||||
|
MultisigMessageStore* mmsStore() const;
|
||||||
|
|
||||||
// ##### Transaction proofs #####
|
// ##### Transaction proofs #####
|
||||||
|
|
||||||
|
@ -386,6 +399,20 @@ public:
|
||||||
|
|
||||||
QString make_uri(const QString &address, quint64 &amount, const QString &description, const QString &recipient) const;
|
QString make_uri(const QString &address, quint64 &amount, const QString &description, const QString &recipient) const;
|
||||||
|
|
||||||
|
// ##### Multisig #####
|
||||||
|
|
||||||
|
void setMMSRefreshEnabled(bool enabled);
|
||||||
|
|
||||||
|
bool hasMultisigPartialKeyImages();
|
||||||
|
QStringList getMultisigSigners();
|
||||||
|
|
||||||
|
QString getMultisigSignerConfig();
|
||||||
|
quint32 multisigSigners();
|
||||||
|
quint32 multisigThreshold();
|
||||||
|
|
||||||
|
// QString originalPrimaryAddress();
|
||||||
|
// QString originalSecretViewkey();
|
||||||
|
|
||||||
// ##### Misc / Unused #####
|
// ##### Misc / Unused #####
|
||||||
|
|
||||||
quint64 getBytesReceived() const;
|
quint64 getBytesReceived() const;
|
||||||
|
@ -498,6 +525,10 @@ private:
|
||||||
Coins *m_coins;
|
Coins *m_coins;
|
||||||
CoinsModel *m_coinsModel;
|
CoinsModel *m_coinsModel;
|
||||||
|
|
||||||
|
MultisigMessageStore *m_mmsStore;
|
||||||
|
MultisigMessageModel *m_mmsModel;
|
||||||
|
MultisigIncomingTxModel *m_msIncomingTxModel;
|
||||||
|
|
||||||
QMutex m_asyncMutex;
|
QMutex m_asyncMutex;
|
||||||
QString m_daemonUsername;
|
QString m_daemonUsername;
|
||||||
QString m_daemonPassword;
|
QString m_daemonPassword;
|
||||||
|
@ -505,6 +536,8 @@ private:
|
||||||
QMutex m_proxyMutex;
|
QMutex m_proxyMutex;
|
||||||
std::atomic<bool> m_refreshNow;
|
std::atomic<bool> m_refreshNow;
|
||||||
std::atomic<bool> m_refreshEnabled;
|
std::atomic<bool> m_refreshEnabled;
|
||||||
|
std::atomic<bool> m_mmsRefreshEnabled;
|
||||||
|
std::chrono::seconds m_refreshInterval{10};
|
||||||
WalletListenerImpl *m_walletListener;
|
WalletListenerImpl *m_walletListener;
|
||||||
FutureScheduler m_scheduler;
|
FutureScheduler m_scheduler;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "WalletListenerImpl.h"
|
#include "WalletListenerImpl.h"
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
#include "WalletManager.h"
|
#include "WalletManager.h"
|
||||||
|
#include "MultisigMessageStore.h"
|
||||||
|
|
||||||
WalletListenerImpl::WalletListenerImpl(Wallet * w)
|
WalletListenerImpl::WalletListenerImpl(Wallet * w)
|
||||||
: m_wallet(w)
|
: m_wallet(w)
|
||||||
|
@ -31,6 +32,11 @@ void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
|
||||||
QString qTxId = QString::fromStdString(txId);
|
QString qTxId = QString::fromStdString(txId);
|
||||||
qDebug() << Q_FUNC_INFO << qTxId << " " << WalletManager::displayAmount(amount);
|
qDebug() << Q_FUNC_INFO << qTxId << " " << WalletManager::displayAmount(amount);
|
||||||
|
|
||||||
|
if (m_wallet->isMultisig()) {
|
||||||
|
// TODO: causes too many exports
|
||||||
|
m_wallet->mmsStore()->exportMultisig();
|
||||||
|
}
|
||||||
|
|
||||||
emit m_wallet->moneyReceived(qTxId, amount);
|
emit m_wallet->moneyReceived(qTxId, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,9 +95,12 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
|
||||||
auto wallet = new Wallet(w);
|
auto wallet = new Wallet(w);
|
||||||
|
|
||||||
// move wallet to the GUI thread. Otherwise it wont be emitting signals
|
// move wallet to the GUI thread. Otherwise it wont be emitting signals
|
||||||
|
qDebug() << wallet->thread();
|
||||||
|
qDebug() << qApp->thread();
|
||||||
if (wallet->thread() != qApp->thread()) {
|
if (wallet->thread() != qApp->thread()) {
|
||||||
wallet->moveToThread(qApp->thread());
|
wallet->moveToThread(qApp->thread());
|
||||||
}
|
}
|
||||||
|
qDebug() << wallet->thread();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
@ -174,6 +177,16 @@ void WalletManager::createWalletFromDeviceAsync(const QString &path, const QStri
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wallet* WalletManager::restoreMultisigWallet(const QString &path, const QString &password, NetworkType::Type nettype,
|
||||||
|
const QString &multisigSeed, const QString &mmsRecovery, quint64 restoreHeight,
|
||||||
|
quint64 kdfRounds,
|
||||||
|
const QString &subaddressLookahead)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_mutex);
|
||||||
|
Monero::Wallet * w = m_pimpl->recoverMultisigWallet(path.toStdString(), password.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, multisigSeed.toStdString(), mmsRecovery.toStdString(), kdfRounds, subaddressLookahead.toStdString());
|
||||||
|
return new Wallet(w);
|
||||||
|
}
|
||||||
|
|
||||||
bool WalletManager::walletExists(const QString &path) const
|
bool WalletManager::walletExists(const QString &path) const
|
||||||
{
|
{
|
||||||
return m_pimpl->walletExists(path.toStdString());
|
return m_pimpl->walletExists(path.toStdString());
|
||||||
|
|
|
@ -58,6 +58,15 @@ public:
|
||||||
const QString &subaddressLookahead = ""
|
const QString &subaddressLookahead = ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Wallet * restoreMultisigWallet(const QString &path,
|
||||||
|
const QString &password,
|
||||||
|
NetworkType::Type nettype,
|
||||||
|
const QString &multisigSeed,
|
||||||
|
const QString &mmsRecovery,
|
||||||
|
quint64 restoreHeight = 0,
|
||||||
|
quint64 kdfRounds = 1,
|
||||||
|
const QString &subaddressLookahead = "");
|
||||||
|
|
||||||
Wallet * createDeterministicWalletFromSpendKey(const QString &path,
|
Wallet * createDeterministicWalletFromSpendKey(const QString &path,
|
||||||
const QString &password,
|
const QString &password,
|
||||||
const QString &language,
|
const QString &language,
|
||||||
|
|
|
@ -124,6 +124,18 @@ QString CoinsInfo::txNote() const {
|
||||||
return m_txNote;
|
return m_txNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CoinsInfo::keyImagePartial() const {
|
||||||
|
return m_keyImagePartial;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoinsInfo::haveMultisigK() const {
|
||||||
|
return m_haveMultisigK;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList CoinsInfo::multisigInfo() const {
|
||||||
|
return m_multisigInfo;
|
||||||
|
}
|
||||||
|
|
||||||
CoinsInfo::CoinsInfo(QObject *parent)
|
CoinsInfo::CoinsInfo(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_blockHeight(0)
|
, m_blockHeight(0)
|
||||||
|
@ -142,6 +154,8 @@ CoinsInfo::CoinsInfo(QObject *parent)
|
||||||
, m_unlocked(false)
|
, m_unlocked(false)
|
||||||
, m_coinbase(false)
|
, m_coinbase(false)
|
||||||
, m_change(false)
|
, m_change(false)
|
||||||
|
, m_keyImagePartial(false)
|
||||||
|
, m_haveMultisigK(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ public:
|
||||||
QString description() const;
|
QString description() const;
|
||||||
bool change() const;
|
bool change() const;
|
||||||
QString txNote() const;
|
QString txNote() const;
|
||||||
|
bool keyImagePartial() const;
|
||||||
|
bool haveMultisigK() const;
|
||||||
|
QStringList multisigInfo() const;
|
||||||
|
|
||||||
void setUnlocked(bool unlocked);
|
void setUnlocked(bool unlocked);
|
||||||
|
|
||||||
|
@ -71,6 +74,9 @@ private:
|
||||||
QString m_description;
|
QString m_description;
|
||||||
bool m_change;
|
bool m_change;
|
||||||
QString m_txNote;
|
QString m_txNote;
|
||||||
|
bool m_keyImagePartial;
|
||||||
|
bool m_haveMultisigK;
|
||||||
|
QStringList m_multisigInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_COINSINFO_H
|
#endif //FEATHER_COINSINFO_H
|
||||||
|
|
16
src/libwalletqt/rows/MultisigMessage.cpp
Normal file
16
src/libwalletqt/rows/MultisigMessage.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Created by user on 1/3/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MultisigMessage.h"
|
||||||
|
|
||||||
|
|
||||||
|
MultisigMessage::MultisigMessage(QObject *parent)
|
||||||
|
: id(0)
|
||||||
|
, signer_index(0)
|
||||||
|
, wallet_height(0)
|
||||||
|
, round(0)
|
||||||
|
, signature_count(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
56
src/libwalletqt/rows/MultisigMessage.h
Normal file
56
src/libwalletqt/rows/MultisigMessage.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// Created by user on 1/3/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FEATHER_MULTISIGMESSAGE_H
|
||||||
|
#define FEATHER_MULTISIGMESSAGE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
//uint32_t id;
|
||||||
|
//message_type type;
|
||||||
|
//message_direction direction;
|
||||||
|
//std::string content;
|
||||||
|
//uint64_t created;
|
||||||
|
//uint64_t modified;
|
||||||
|
//uint64_t sent;
|
||||||
|
//uint32_t signer_index;
|
||||||
|
//crypto::hash hash;
|
||||||
|
//message_state state;
|
||||||
|
//uint32_t wallet_height;
|
||||||
|
//uint32_t round;
|
||||||
|
//uint32_t signature_count;
|
||||||
|
//std::string transport_id;
|
||||||
|
|
||||||
|
class MultisigMessage : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
quint32 id;
|
||||||
|
QString type;
|
||||||
|
QString direction;
|
||||||
|
std::string content;
|
||||||
|
QDateTime created;
|
||||||
|
QDateTime modified;
|
||||||
|
QDateTime sent;
|
||||||
|
quint32 signer_index;
|
||||||
|
QString signer;
|
||||||
|
QString hash;
|
||||||
|
QString state;
|
||||||
|
quint32 wallet_height;
|
||||||
|
quint32 round;
|
||||||
|
quint32 signature_count;
|
||||||
|
QString transport_id;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit MultisigMessage(QObject *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MultisigMessageStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_MULTISIGMESSAGE_H
|
|
@ -164,4 +164,8 @@ QString TransactionRow::rings_formatted() const
|
||||||
rings += "\n\n";
|
rings += "\n\n";
|
||||||
}
|
}
|
||||||
return rings;
|
return rings;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TransactionRow::prefixHash() const {
|
||||||
|
return m_prefixHash;
|
||||||
}
|
}
|
|
@ -50,6 +50,7 @@ public:
|
||||||
QList<QString> destinations() const;
|
QList<QString> destinations() const;
|
||||||
QList<Transfer*> transfers() const;
|
QList<Transfer*> transfers() const;
|
||||||
QString rings_formatted() const;
|
QString rings_formatted() const;
|
||||||
|
QString prefixHash() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit TransactionRow();
|
explicit TransactionRow();
|
||||||
|
@ -76,6 +77,7 @@ private:
|
||||||
QDateTime m_timestamp;
|
QDateTime m_timestamp;
|
||||||
quint64 m_unlockTime;
|
quint64 m_unlockTime;
|
||||||
bool m_coinbase;
|
bool m_coinbase;
|
||||||
|
QString m_prefixHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
27
src/libwalletqt/rows/TxProposal.cpp
Normal file
27
src/libwalletqt/rows/TxProposal.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// Created by user on 3/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TxProposal.h"
|
||||||
|
|
||||||
|
TxProposal::TxProposal(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, messageId(0)
|
||||||
|
, txCount(0)
|
||||||
|
, numSignatures(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TxProposal::errorString() {
|
||||||
|
if (this->txCount == 0) {
|
||||||
|
return {"Tx proposal contains no transactions"};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->txCount > 1) {
|
||||||
|
// the UI doesn't support split transactions
|
||||||
|
return {"Tx proposal contains too many transactions"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
47
src/libwalletqt/rows/TxProposal.h
Normal file
47
src/libwalletqt/rows/TxProposal.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// Created by user on 3/20/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FEATHER_TXPROPOSAL_H
|
||||||
|
#define FEATHER_TXPROPOSAL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
|
||||||
|
class TxProposal : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Status {
|
||||||
|
Pending = 0,
|
||||||
|
Expired,
|
||||||
|
Cant_Sign,
|
||||||
|
Signed,
|
||||||
|
Completed,
|
||||||
|
Frozen,
|
||||||
|
Double_Spend
|
||||||
|
};
|
||||||
|
|
||||||
|
quint64 messageId;
|
||||||
|
QDateTime timestamp;
|
||||||
|
Status status;
|
||||||
|
QStringList spendsOutputs;
|
||||||
|
quint64 txCount;
|
||||||
|
QString txId;
|
||||||
|
quint64 numSignatures;
|
||||||
|
QStringList signers;
|
||||||
|
QString from;
|
||||||
|
qint64 balanceDelta;
|
||||||
|
QString prefixHash;
|
||||||
|
|
||||||
|
QString errorString();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit TxProposal(QObject *parent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MultisigMessageStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_TXPROPOSAL_H
|
16
src/main.cpp
16
src/main.cpp
|
@ -5,7 +5,7 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#include <singleapplication.h>
|
//#include <singleapplication.h>
|
||||||
|
|
||||||
#include "config-feather.h"
|
#include "config-feather.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
@ -88,6 +88,11 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
|
qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Force XCB to deal with 'Could not find the Qt platform plugin "wayland" in ""'
|
||||||
|
#if defined(Q_OS_LINUX) && defined(STATIC)
|
||||||
|
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||||
|
#endif
|
||||||
|
|
||||||
QStringList argv_;
|
QStringList argv_;
|
||||||
for(int i = 0; i != argc; i++){
|
for(int i = 0; i != argc; i++){
|
||||||
argv_ << QString::fromStdString(argv[i]);
|
argv_ << QString::fromStdString(argv[i]);
|
||||||
|
@ -137,7 +142,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SingleApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
QApplication::setQuitOnLastWindowClosed(false);
|
QApplication::setQuitOnLastWindowClosed(false);
|
||||||
QApplication::setApplicationName("FeatherWallet");
|
QApplication::setApplicationName("FeatherWallet");
|
||||||
|
@ -243,10 +248,11 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
||||||
|
|
||||||
auto wm = windowManager();
|
auto wm = windowManager();
|
||||||
wm->setEventFilter(&filter);
|
wm->setEventFilter(&filter);
|
||||||
|
wm->raise();
|
||||||
|
|
||||||
QObject::connect(&app, &SingleApplication::instanceStarted, [&wm]() {
|
// QObject::connect(&app, &SingleApplication::instanceStarted, [&wm]() {
|
||||||
wm->raise();
|
// wm->raise();
|
||||||
});
|
// });
|
||||||
|
|
||||||
return QApplication::exec();
|
return QApplication::exec();
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,12 +90,22 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case KeyImageKnown:
|
case KeyImageKnown:
|
||||||
{
|
{
|
||||||
if (cInfo.keyImageKnown()) {
|
if (!cInfo.keyImageKnown() || cInfo.keyImagePartial()) {
|
||||||
result = QVariant(icons()->icon("eye1.png"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
result = QVariant(icons()->icon("eye_blind.png"));
|
result = QVariant(icons()->icon("eye_blind.png"));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
result = QVariant(icons()->icon("eye1.png"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HaveMultisigK:
|
||||||
|
{
|
||||||
|
if (cInfo.haveMultisigK()) {
|
||||||
|
result = QVariant(icons()->icon("status_connected.svg"));
|
||||||
|
} else {
|
||||||
|
result = QVariant(icons()->icon("status_lagging.svg"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +126,21 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
|
||||||
} else {
|
} else {
|
||||||
result = "Key image unknown. Outgoing transactions that include this output will not be detected.";
|
result = "Key image unknown. Outgoing transactions that include this output will not be detected.";
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HaveMultisigK:
|
||||||
|
{
|
||||||
|
if (cInfo.haveMultisigK()) {
|
||||||
|
result = "We can spend this output in a transaction proposal.";
|
||||||
|
} else {
|
||||||
|
result = "We have recently spent this output in a transaction proposal.";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MultisigInfo:
|
||||||
|
{
|
||||||
|
result = cInfo.multisigInfo().join(", ") + " can sign a transaction that spends this output";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cInfo.frozen()) {
|
if (cInfo.frozen()) {
|
||||||
|
@ -148,6 +173,8 @@ QVariant CoinsModel::headerData(int section, Qt::Orientation orientation, int ro
|
||||||
switch(section) {
|
switch(section) {
|
||||||
case PubKey:
|
case PubKey:
|
||||||
return QString("Pub Key");
|
return QString("Pub Key");
|
||||||
|
case MultisigInfo:
|
||||||
|
return QString("Multisig info");
|
||||||
case TxID:
|
case TxID:
|
||||||
return QString("TxID");
|
return QString("TxID");
|
||||||
case BlockHeight:
|
case BlockHeight:
|
||||||
|
@ -217,6 +244,10 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
|
||||||
{
|
{
|
||||||
case KeyImageKnown:
|
case KeyImageKnown:
|
||||||
return "";
|
return "";
|
||||||
|
case HaveMultisigK:
|
||||||
|
return "";
|
||||||
|
case MultisigInfo:
|
||||||
|
return cInfo.multisigInfo().join(", ");
|
||||||
case PubKey:
|
case PubKey:
|
||||||
return cInfo.pubKey().mid(0,8);
|
return cInfo.pubKey().mid(0,8);
|
||||||
case TxID:
|
case TxID:
|
||||||
|
|
|
@ -22,6 +22,8 @@ public:
|
||||||
enum ModelColumn
|
enum ModelColumn
|
||||||
{
|
{
|
||||||
KeyImageKnown = 0,
|
KeyImageKnown = 0,
|
||||||
|
HaveMultisigK,
|
||||||
|
MultisigInfo,
|
||||||
PubKey,
|
PubKey,
|
||||||
TxID,
|
TxID,
|
||||||
Address,
|
Address,
|
||||||
|
|
181
src/model/MultisigIncomingTxModel.cpp
Normal file
181
src/model/MultisigIncomingTxModel.cpp
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
////
|
||||||
|
//// Created by user on 1/9/24.
|
||||||
|
////
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MultisigIncomingTxModel.h"
|
||||||
|
#include "libwalletqt/rows/MultisigMessage.h"
|
||||||
|
#include "MultisigMessageStore.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
#include "libwalletqt/WalletManager.h"
|
||||||
|
#include "rows/TxProposal.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
MultisigIncomingTxModel::MultisigIncomingTxModel(QObject *parent, MultisigMessageStore *store)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_store(store)
|
||||||
|
{
|
||||||
|
connect(m_store, &MultisigMessageStore::refreshStarted, this, &MultisigIncomingTxModel::startReset);
|
||||||
|
connect(m_store, &MultisigMessageStore::refreshFinished, this, &MultisigIncomingTxModel::endReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultisigIncomingTxModel::startReset(){
|
||||||
|
beginResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultisigIncomingTxModel::endReset(){
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultisigIncomingTxModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return m_store->txProposalCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultisigIncomingTxModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ModelColumn::COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MultisigIncomingTxModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!m_store) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!index.isValid() || index.row() < 0 || static_cast<quint64>(index.row()) >= m_store->count())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
QVariant result;
|
||||||
|
|
||||||
|
bool found = m_store->txProposal(index.row(), [this, &index, &result, &role](const TxProposal &msg) {
|
||||||
|
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
|
||||||
|
result = getMessageInfo(msg, index.column(), role);
|
||||||
|
}
|
||||||
|
else if (role == Qt::DecorationRole) {
|
||||||
|
switch (index.column()) {
|
||||||
|
case ModelColumn::Status:
|
||||||
|
{
|
||||||
|
switch (msg.status) {
|
||||||
|
case TxProposal::Status::Completed: {
|
||||||
|
result = QVariant(icons()->icon("confirmed.svg"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TxProposal::Status::Cant_Sign:
|
||||||
|
case TxProposal::Status::Double_Spend:
|
||||||
|
{
|
||||||
|
result = QVariant(icons()->icon("expired.png"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TxProposal::Status::Frozen: {
|
||||||
|
result = QVariant(icons()->icon("freeze.png"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TxProposal::Status::Signed: {
|
||||||
|
result = QVariant(icons()->icon("sign.png"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
result = QVariant(icons()->icon("arrow.svg"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MultisigIncomingTxModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role != Qt::DisplayRole) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
switch(section) {
|
||||||
|
case Status:
|
||||||
|
return QString("Status");
|
||||||
|
case Date:
|
||||||
|
return QString("Date");
|
||||||
|
case Signatures:
|
||||||
|
return QString("Signatures");
|
||||||
|
case SpendsOutputs:
|
||||||
|
return QString("Spends Outputs");
|
||||||
|
case TxCount:
|
||||||
|
return QString("Tx Count");
|
||||||
|
case TxId:
|
||||||
|
return QString("Txid");
|
||||||
|
case PrefixHash:
|
||||||
|
return QString("Prefix hash");
|
||||||
|
case Amount:
|
||||||
|
return QString("Amount");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariant MultisigIncomingTxModel::getMessageInfo(const TxProposal &msg, int column, int role) const
|
||||||
|
{
|
||||||
|
switch (column)
|
||||||
|
{
|
||||||
|
case Status: {
|
||||||
|
switch (msg.status) {
|
||||||
|
case TxProposal::Status::Completed:
|
||||||
|
return "Complete";
|
||||||
|
case TxProposal::Status::Cant_Sign:
|
||||||
|
return "Can't sign";
|
||||||
|
case TxProposal::Status::Pending:
|
||||||
|
return "Pending";
|
||||||
|
case TxProposal::Status::Expired:
|
||||||
|
return "Expired";
|
||||||
|
case TxProposal::Status::Frozen:
|
||||||
|
return "Frozen";
|
||||||
|
case TxProposal::Status::Signed:
|
||||||
|
return "Signed";
|
||||||
|
case TxProposal::Status::Double_Spend:
|
||||||
|
return "Double spend";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Date:
|
||||||
|
return msg.timestamp.toString(QString("%1 %2 ").arg(conf()->get(Config::dateFormat).toString(),
|
||||||
|
conf()->get(Config::timeFormat).toString()));
|
||||||
|
case Signatures:
|
||||||
|
return msg.numSignatures;
|
||||||
|
case MessageID:
|
||||||
|
return msg.messageId;
|
||||||
|
case SpendsOutputs:
|
||||||
|
return msg.spendsOutputs.join(", ");
|
||||||
|
case TxCount:
|
||||||
|
return msg.txCount;
|
||||||
|
case TxId:
|
||||||
|
return msg.txId.mid(0, 8);
|
||||||
|
case PrefixHash:
|
||||||
|
return msg.prefixHash.mid(0, 8);
|
||||||
|
case Amount:
|
||||||
|
return WalletManager::displayAmount(msg.balanceDelta);
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
qCritical() << "Unimplemented role";
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
56
src/model/MultisigIncomingTxModel.h
Normal file
56
src/model/MultisigIncomingTxModel.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
////
|
||||||
|
//// Created by user on 1/9/24.
|
||||||
|
////
|
||||||
|
//
|
||||||
|
#ifndef FEATHER_MULTISIGINCOMINGTXMODEL_H
|
||||||
|
#define FEATHER_MULTISIGINCOMINGTXMODEL_H
|
||||||
|
|
||||||
|
#include <wallet/api/wallet2_api.h>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
class MultisigMessageStore;
|
||||||
|
class MultisigMessage;
|
||||||
|
class TxProposal;
|
||||||
|
|
||||||
|
class MultisigIncomingTxModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ModelColumn
|
||||||
|
{
|
||||||
|
Status,
|
||||||
|
Date,
|
||||||
|
MessageID,
|
||||||
|
Signatures,
|
||||||
|
SpendsOutputs,
|
||||||
|
TxCount,
|
||||||
|
TxId,
|
||||||
|
PrefixHash,
|
||||||
|
Amount,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MultisigIncomingTxModel(QObject *parent, MultisigMessageStore *store);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startReset();
|
||||||
|
void endReset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant getMessageInfo(const TxProposal &msg, int column, int role) const;
|
||||||
|
|
||||||
|
MultisigMessageStore *m_store;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_MULTISIGINCOMINGTXMODEL_H
|
152
src/model/MultisigMessageModel.cpp
Normal file
152
src/model/MultisigMessageModel.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "libwalletqt/rows/MultisigMessage.h"
|
||||||
|
#include "MultisigMessageModel.h"
|
||||||
|
#include "MultisigMessageStore.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "utils/ColorScheme.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
#include "libwalletqt/WalletManager.h"
|
||||||
|
|
||||||
|
#include <QBrush>
|
||||||
|
|
||||||
|
MultisigMessageModel::MultisigMessageModel(QObject *parent, MultisigMessageStore *store)
|
||||||
|
: QAbstractTableModel(parent)
|
||||||
|
, m_store(store)
|
||||||
|
{
|
||||||
|
connect(m_store, &MultisigMessageStore::refreshStarted, this, &MultisigMessageModel::startReset);
|
||||||
|
connect(m_store, &MultisigMessageStore::refreshFinished, this, &MultisigMessageModel::endReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultisigMessageModel::startReset(){
|
||||||
|
beginResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultisigMessageModel::endReset(){
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultisigMessageModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return m_store->count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultisigMessageModel::columnCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ModelColumn::COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MultisigMessageModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!m_store) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!index.isValid() || index.row() < 0 || static_cast<quint64>(index.row()) >= m_store->count())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
QVariant result;
|
||||||
|
|
||||||
|
bool found = m_store->message(index.row(), [this, &index, &result, &role](const MultisigMessage &msg) {
|
||||||
|
if(role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::UserRole) {
|
||||||
|
result = getMessageInfo(msg, index.column(), role);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!found) {
|
||||||
|
qCritical("%s: internal error: no transaction info for index %d", __FUNCTION__, index.row());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant MultisigMessageModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role != Qt::DisplayRole) {
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
{
|
||||||
|
switch(section) {
|
||||||
|
case Id:
|
||||||
|
return QString("Id");
|
||||||
|
case Type:
|
||||||
|
return QString("Type");
|
||||||
|
case Direction:
|
||||||
|
return QString("Direction");
|
||||||
|
case Created:
|
||||||
|
return QString("Created");
|
||||||
|
case Modified:
|
||||||
|
return QString("Modified");
|
||||||
|
case Sent:
|
||||||
|
return QString("Sent");
|
||||||
|
case Signer:
|
||||||
|
return QString("Signer");
|
||||||
|
case Hash:
|
||||||
|
return QString("Hash");
|
||||||
|
case State:
|
||||||
|
return QString("State");
|
||||||
|
case WalletHeight:
|
||||||
|
return QString("Wallet Height");
|
||||||
|
case Round:
|
||||||
|
return QString("Round");
|
||||||
|
case SignatureCount:
|
||||||
|
return QString("Signature Count");
|
||||||
|
case TransportId:
|
||||||
|
return QString("Transport ID");
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariant MultisigMessageModel::getMessageInfo(const MultisigMessage &msg, int column, int role) const
|
||||||
|
{
|
||||||
|
switch (column)
|
||||||
|
{
|
||||||
|
case Id:
|
||||||
|
return msg.id;
|
||||||
|
case Type:
|
||||||
|
return msg.type;
|
||||||
|
case Direction:
|
||||||
|
return msg.direction;
|
||||||
|
case Created:
|
||||||
|
return msg.created;
|
||||||
|
case Modified:
|
||||||
|
return msg.modified;
|
||||||
|
case Sent:
|
||||||
|
return msg.sent;
|
||||||
|
case Signer:
|
||||||
|
return msg.signer;
|
||||||
|
case Hash: {
|
||||||
|
return msg.hash;
|
||||||
|
}
|
||||||
|
case State:
|
||||||
|
return msg.state;
|
||||||
|
case WalletHeight:
|
||||||
|
return msg.wallet_height;
|
||||||
|
case Round:
|
||||||
|
{
|
||||||
|
return msg.round;
|
||||||
|
}
|
||||||
|
case SignatureCount:
|
||||||
|
return msg.signature_count;
|
||||||
|
case TransportId:
|
||||||
|
return msg.transport_id;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
qCritical() << "Unimplemented role";
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
58
src/model/MultisigMessageModel.h
Normal file
58
src/model/MultisigMessageModel.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
//
|
||||||
|
// Created by user on 1/3/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FEATHER_MULTISIGMESSAGEMODEL_H
|
||||||
|
#define FEATHER_MULTISIGMESSAGEMODEL_H
|
||||||
|
|
||||||
|
#include <wallet/api/wallet2_api.h>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
class MultisigMessageStore;
|
||||||
|
class MultisigMessage;
|
||||||
|
|
||||||
|
class MultisigMessageModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum ModelColumn
|
||||||
|
{
|
||||||
|
Id = 0,
|
||||||
|
Type,
|
||||||
|
Direction,
|
||||||
|
Created,
|
||||||
|
Modified,
|
||||||
|
Sent,
|
||||||
|
Signer,
|
||||||
|
Hash,
|
||||||
|
State,
|
||||||
|
WalletHeight,
|
||||||
|
Round,
|
||||||
|
SignatureCount,
|
||||||
|
TransportId,
|
||||||
|
COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit MultisigMessageModel(QObject *parent, MultisigMessageStore *store);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startReset();
|
||||||
|
void endReset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant getMessageInfo(const MultisigMessage &msg, int column, int role) const;
|
||||||
|
|
||||||
|
MultisigMessageStore *m_store;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_MULTISIGMESSAGEMODEL_H
|
|
@ -165,6 +165,10 @@ QVariant TransactionHistoryModel::parseTransactionInfo(const TransactionRow &tIn
|
||||||
amount = (tInfo.balanceDelta() < 0) ? amount : "+" + amount;
|
amount = (tInfo.balanceDelta() < 0) ? amount : "+" + amount;
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
|
// case Column::TxPrefixHash:
|
||||||
|
// {
|
||||||
|
// return tInfo.prefixHash().mid(0, 8);
|
||||||
|
// }
|
||||||
case Column::TxID: {
|
case Column::TxID: {
|
||||||
if (conf()->get(Config::historyShowFullTxid).toBool()) {
|
if (conf()->get(Config::historyShowFullTxid).toBool()) {
|
||||||
return tInfo.hash();
|
return tInfo.hash();
|
||||||
|
@ -217,6 +221,8 @@ QVariant TransactionHistoryModel::headerData(int section, Qt::Orientation orient
|
||||||
return QString("Amount");
|
return QString("Amount");
|
||||||
case Column::TxID:
|
case Column::TxID:
|
||||||
return QString("Txid");
|
return QString("Txid");
|
||||||
|
// case Column::TxPrefixHash:
|
||||||
|
// return QString("Prefix hash");
|
||||||
case Column::FiatAmount:
|
case Column::FiatAmount:
|
||||||
return QString("Fiat");
|
return QString("Fiat");
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -513,6 +513,14 @@ QString displayAddress(const QString& address, int sections, const QString& sep)
|
||||||
return list.join(sep);
|
return list.join(sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString chunkAddress(const QString& address) {
|
||||||
|
QStringList list;
|
||||||
|
for (int i = 0; i < 19; i+= 1) {
|
||||||
|
list << address.mid(i*5, 5);
|
||||||
|
}
|
||||||
|
return list.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount) {
|
QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount) {
|
||||||
QTextCharFormat rec;
|
QTextCharFormat rec;
|
||||||
if (index.isPrimary()) {
|
if (index.isPrimary()) {
|
||||||
|
@ -684,4 +692,13 @@ QString formatSyncStatus(quint64 height, quint64 target, bool daemonSync) {
|
||||||
|
|
||||||
return "Synchronized";
|
return "Synchronized";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString formatRestoreHeight(Wallet *wallet) {
|
||||||
|
if (!wallet) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime restoreDate = appData()->restoreHeights[constants::networkType]->heightToDate(wallet->getWalletCreationHeight());
|
||||||
|
return QString("%1 (%2)").arg(QString::number(wallet->getWalletCreationHeight()), restoreDate.toString("yyyy-MM-dd"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ namespace Utils
|
||||||
void externalLinkWarning(QWidget *parent, const QString &url);
|
void externalLinkWarning(QWidget *parent, const QString &url);
|
||||||
|
|
||||||
QString displayAddress(const QString& address, int sections = 3, const QString & sep = " ");
|
QString displayAddress(const QString& address, int sections = 3, const QString & sep = " ");
|
||||||
|
QString chunkAddress(const QString& address);
|
||||||
QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount);
|
QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount);
|
||||||
|
|
||||||
QFont getMonospaceFont();
|
QFont getMonospaceFont();
|
||||||
|
@ -116,6 +117,7 @@ namespace Utils
|
||||||
void clearLayout(QLayout *layout, bool deleteWidgets = true);
|
void clearLayout(QLayout *layout, bool deleteWidgets = true);
|
||||||
|
|
||||||
QString formatSyncStatus(quint64 height, quint64 target, bool daemonSync = false);
|
QString formatSyncStatus(quint64 height, quint64 target, bool daemonSync = false);
|
||||||
|
QString formatRestoreHeight(Wallet *wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //FEATHER_UTILS_H
|
#endif //FEATHER_UTILS_H
|
||||||
|
|
|
@ -21,6 +21,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||||
{Config::warnOnStagenet,{QS("warnOnStagenet"), true}},
|
{Config::warnOnStagenet,{QS("warnOnStagenet"), true}},
|
||||||
{Config::warnOnTestnet,{QS("warnOnTestnet"), true}},
|
{Config::warnOnTestnet,{QS("warnOnTestnet"), true}},
|
||||||
{Config::warnOnKiImport,{QS("warnOnKiImport"), true}},
|
{Config::warnOnKiImport,{QS("warnOnKiImport"), true}},
|
||||||
|
{Config::warnOnMultisigExperimental,{QS("warnOnMultisigExperimental"), true}},
|
||||||
{Config::logLevel,{QS("logLevel"), 0}},
|
{Config::logLevel,{QS("logLevel"), 0}},
|
||||||
|
|
||||||
{Config::homeWidget,{QS("homeWidget"), "ccs"}},
|
{Config::homeWidget,{QS("homeWidget"), "ccs"}},
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
warnOnStagenet,
|
warnOnStagenet,
|
||||||
warnOnTestnet,
|
warnOnTestnet,
|
||||||
warnOnKiImport,
|
warnOnKiImport,
|
||||||
|
warnOnMultisigExperimental,
|
||||||
|
|
||||||
homeWidget,
|
homeWidget,
|
||||||
donateBeg,
|
donateBeg,
|
||||||
|
|
21
src/widgets/MultisigSetupWidget.cpp
Normal file
21
src/widgets/MultisigSetupWidget.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "MultisigSetupWidget.h"
|
||||||
|
#include "ui_MultisigSetupWidget.h"
|
||||||
|
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
|
||||||
|
MultisigSetupWidget::MultisigSetupWidget(QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, ui(new Ui::MultisigSetupWidget)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
// char setupKey[35];
|
||||||
|
//
|
||||||
|
// auto key = rct::rct2sk(rct::skGen()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MultisigSetupWidget::~MultisigSetupWidget() = default;
|
30
src/widgets/MultisigSetupWidget.h
Normal file
30
src/widgets/MultisigSetupWidget.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_MULTISIGSETUPWIDGET_H
|
||||||
|
#define FEATHER_MULTISIGSETUPWIDGET_H
|
||||||
|
|
||||||
|
#include <QItemSelection>
|
||||||
|
#include <QTreeView>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "model/NodeModel.h"
|
||||||
|
#include "utils/nodes.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class MultisigSetupWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MultisigSetupWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MultisigSetupWidget(QWidget *parent = nullptr);
|
||||||
|
~MultisigSetupWidget();
|
||||||
|
|
||||||
|
QScopedPointer<Ui::MultisigSetupWidget> ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_MULTISIGSETUPWIDGET_H
|
415
src/widgets/MultisigSetupWidget.ui
Normal file
415
src/widgets/MultisigSetupWidget.ui
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MultisigSetupWidget</class>
|
||||||
|
<widget class="QWidget" name="MultisigSetupWidget">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>720</width>
|
||||||
|
<height>361</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="page_3">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:700;">Create setup key</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="text">
|
||||||
|
<string>Click 'Next' if you already have a setup key.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>Threshold:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signers:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="text">
|
||||||
|
<string>Setup key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_generatedSetupKey">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_copySetupKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Copy</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spin_threshold">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>16</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Number of signatures required to spend funds.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QSpinBox" name="spin_signers">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>16</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>3</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPushButton" name="btn_generateSetupKey">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Generate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:700;">Multisig setup</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Setup key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_setupKey"/>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Channel:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_channel"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Username:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="line_name"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Threshold:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_threshold">
|
||||||
|
<property name="text">
|
||||||
|
<string>?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signers:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_signers">
|
||||||
|
<property name="text">
|
||||||
|
<string>?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_2">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:700;">Waiting for participants</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="tree_participants">
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Name</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string>Address</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_4">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p><span style=" font-weight:700;">Multisig wallet created successfully</span></p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout_3">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="text">
|
||||||
|
<string>Address:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="lineEdit_2"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_17">
|
||||||
|
<property name="text">
|
||||||
|
<string>All participants must verify that the address matches via a secure channel.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_18">
|
||||||
|
<property name="text">
|
||||||
|
<string>Do not proceed until you are certain that every signer has the same address.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_next">
|
||||||
|
<property name="text">
|
||||||
|
<string>Next</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -72,6 +72,20 @@ void TxDetailsSimple::setDetails(Wallet *wallet, PendingTransaction *tx, const Q
|
||||||
ui->label_fee->setStyleSheet(ColorScheme::RED.asStylesheet(true));
|
ui->label_fee->setStyleSheet(ColorScheme::RED.asStylesheet(true));
|
||||||
ui->label_fee->setToolTip("Unrealistic fee. You may be connected to a malicious node.");
|
ui->label_fee->setToolTip("Unrealistic fee. You may be connected to a malicious node.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (wallet->isMultisig()) {
|
||||||
|
// ui->btn_
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (wallet->isMultisig()) {
|
||||||
|
// tx->
|
||||||
|
//
|
||||||
|
// auto multisigState = wallet->multisig();
|
||||||
|
// if (multisigState.isMultisig && m_signers.size() < multisigState.threshold) {
|
||||||
|
// throw runtime_error("Not enough signers to send multisig transaction");
|
||||||
|
// }
|
||||||
|
// ui->label_signatures->setText()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
TxDetailsSimple::~TxDetailsSimple() = default;
|
TxDetailsSimple::~TxDetailsSimple() = default;
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>386</width>
|
<width>576</width>
|
||||||
<height>152</height>
|
<height>230</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -109,6 +109,67 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_multisig">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QFormLayout" name="formLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Signatures:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label_signatures">
|
||||||
|
<property name="text">
|
||||||
|
<string>1/?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Send to:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="combo_sendTo">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
|
60
src/wizard/PageCreateWalletType.ui
Normal file
60
src/wizard/PageCreateWalletType.ui
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageCreateWalletType</class>
|
||||||
|
<widget class="QWizardPage" name="PageCreateWalletType">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>417</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Select wallet type to create:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Standard</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radioButton_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Multisig</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>275</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -16,14 +16,13 @@ PageHardwareDevice::PageHardwareDevice(WizardFields *fields, QWidget *parent)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->combo_deviceType->addItem("Ledger Nano S (PLUS) / X", DeviceType::LEDGER);
|
this->setTitle("Restore from hardware device");
|
||||||
ui->combo_deviceType->addItem("Trezor Model T / Safe 3", DeviceType::TREZOR);
|
|
||||||
|
|
||||||
connect(ui->btnOptions, &QPushButton::clicked, this, &PageHardwareDevice::onOptionsClicked);
|
connect(ui->btnOptions, &QPushButton::clicked, this, &PageHardwareDevice::onOptionsClicked);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageHardwareDevice::initializePage() {
|
void PageHardwareDevice::initializePage() {
|
||||||
ui->radioNewWallet->setChecked(true);
|
ui->radio_create->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PageHardwareDevice::nextId() const {
|
int PageHardwareDevice::nextId() const {
|
||||||
|
@ -35,8 +34,13 @@ int PageHardwareDevice::nextId() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageHardwareDevice::validatePage() {
|
bool PageHardwareDevice::validatePage() {
|
||||||
m_fields->deviceType = static_cast<DeviceType>(ui->combo_deviceType->currentData().toInt());
|
if (ui->radio_ledger->isChecked()) {
|
||||||
m_fields->showSetRestoreHeightPage = ui->radioRestoreWallet->isChecked();
|
m_fields->deviceType = DeviceType::LEDGER;
|
||||||
|
} else {
|
||||||
|
m_fields->deviceType = DeviceType::TREZOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fields->showSetRestoreHeightPage = ui->radio_restore->isChecked();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,31 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Select device type:</string>
|
<string>Select device type: </string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_ledger">
|
||||||
|
<property name="text">
|
||||||
|
<string>Ledger (Nano S, Nano S+, Nano X)</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_trezor">
|
||||||
|
<property name="text">
|
||||||
|
<string>Trezor (Model T, Safe 3)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="combo_deviceType"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="Line" name="line">
|
<widget class="Line" name="line">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -32,79 +48,31 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="radioNewWallet">
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
<property name="text">
|
<property name="title">
|
||||||
<string>Create a new wallet file from device</string>
|
<string>Does the wallet currently hold any funds? </string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_create">
|
||||||
|
<property name="text">
|
||||||
|
<string>No</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_restore">
|
||||||
|
<property name="text">
|
||||||
|
<string>Yes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use this option if the keys on the device hold no funds. (i.e. this wallet was never used before)</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioRestoreWallet">
|
|
||||||
<property name="text">
|
|
||||||
<string>Restore a wallet from device</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Fixed</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>If this option is selected, you will be asked specify a wallet creation date or restore height next.</string>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
39
src/wizard/PageKeyType.cpp
Normal file
39
src/wizard/PageKeyType.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageKeyType.h"
|
||||||
|
#include "WalletWizard.h"
|
||||||
|
#include "ui_PageKeyType.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
|
||||||
|
PageKeyType::PageKeyType(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageKeyType)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
// this->setTitle("Restore multisig wallet");
|
||||||
|
this->setTitle("Recover wallet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageKeyType::initializePage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageKeyType::nextId() const {
|
||||||
|
if (ui->radio_seed) {
|
||||||
|
return WalletWizard::Page_WalletRestoreSeed;
|
||||||
|
} else {
|
||||||
|
return WalletWizard::Page_WalletRestoreKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageKeyType::validatePage() {
|
||||||
|
m_fields->clearFields();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
32
src/wizard/PageKeyType.h
Normal file
32
src/wizard/PageKeyType.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEKEYTYPE_H
|
||||||
|
#define FEATHER_PAGEKEYTYPE_H
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageKeyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageKeyType : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageKeyType(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageKeyType *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEKEYTYPE_H
|
67
src/wizard/PageKeyType.ui
Normal file
67
src/wizard/PageKeyType.ui
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageKeyType</class>
|
||||||
|
<widget class="QWizardPage" name="PageKeyType">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>485</width>
|
||||||
|
<height>329</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Select key type:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_seed">
|
||||||
|
<property name="text">
|
||||||
|
<string>Seed</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_deterministic">
|
||||||
|
<property name="text">
|
||||||
|
<string>Spendkey (deterministic)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_nondeterministic">
|
||||||
|
<property name="text">
|
||||||
|
<string>Spendkey + Viewkey (non-deterministic)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -21,13 +21,34 @@ PageMenu::PageMenu(WizardFields *fields, WalletKeysFilesModel *wallets, QWidget
|
||||||
ui->label_version->setText(QString("Feather %1 — by dsc & tobtoht").arg(FEATHER_VERSION));
|
ui->label_version->setText(QString("Feather %1 — by dsc & tobtoht").arg(FEATHER_VERSION));
|
||||||
|
|
||||||
QString settingsSkin = conf()->get(Config::skin).toString();
|
QString settingsSkin = conf()->get(Config::skin).toString();
|
||||||
|
|
||||||
|
// connect(ui->btn_create, &QPushButton::clicked, [this]{
|
||||||
|
// m_fields->mode = WizardMode::CreateWallet;
|
||||||
|
// m_fields->modeText = "Create wallet";
|
||||||
|
// m_nextPage = WalletWizard::Page_Recover;
|
||||||
|
// wizard()->button(QWizard::NextButton)->click();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// connect(ui->btn_open, &QPushButton::clicked, [this]{
|
||||||
|
// m_fields->mode = WizardMode::OpenWallet;
|
||||||
|
// m_fields->modeText = "Open wallet";
|
||||||
|
// m_nextPage = WalletWizard::Page_OpenWallet;
|
||||||
|
// wizard()->button(QWizard::NextButton)->click();
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// connect(ui->btn_restore, &QPushButton::clicked, [this]{
|
||||||
|
//// m_fields->mode = WizardMode::RecoverWallet;
|
||||||
|
// m_fields->modeText = "Restore wallet";
|
||||||
|
// m_nextPage = WalletWizard::Page_Recover;
|
||||||
|
// wizard()->button(QWizard::NextButton)->click();
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageMenu::initializePage() {
|
void PageMenu::initializePage() {
|
||||||
if (m_walletKeysFilesModel->rowCount() > 0) {
|
if (m_walletKeysFilesModel->rowCount() > 0) {
|
||||||
ui->radioOpen->setChecked(true);
|
ui->radio_open->setChecked(true);
|
||||||
} else {
|
} else {
|
||||||
ui->radioCreate->setChecked(true);
|
ui->radio_create->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't show setup wizard again
|
// Don't show setup wizard again
|
||||||
|
@ -35,15 +56,19 @@ void PageMenu::initializePage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int PageMenu::nextId() const {
|
int PageMenu::nextId() const {
|
||||||
if (ui->radioCreate->isChecked())
|
if (ui->radio_create->isChecked())
|
||||||
return WalletWizard::Page_CreateWalletSeed;
|
return WalletWizard::Page_CreateWalletSeed;
|
||||||
if (ui->radioOpen->isChecked())
|
if (ui->radio_createMultisig->isChecked())
|
||||||
|
return WalletWizard::Page_MultisigExperimentalWarning;
|
||||||
|
if (ui->radio_open->isChecked())
|
||||||
return WalletWizard::Page_OpenWallet;
|
return WalletWizard::Page_OpenWallet;
|
||||||
if (ui->radioSeed->isChecked())
|
if (ui->radio_restoreSeed->isChecked())
|
||||||
return WalletWizard::Page_WalletRestoreSeed;
|
return WalletWizard::Page_WalletRestoreSeed;
|
||||||
if (ui->radioViewOnly->isChecked())
|
if (ui->radio_restoreKeys->isChecked())
|
||||||
return WalletWizard::Page_WalletRestoreKeys;
|
return WalletWizard::Page_WalletRestoreKeys;
|
||||||
if (ui->radioCreateFromDevice->isChecked())
|
if (ui->radio_restoreMultisig->isChecked())
|
||||||
|
return WalletWizard::Page_MultisigRestoreSeed;
|
||||||
|
if (ui->radio_restoreHardware->isChecked())
|
||||||
return WalletWizard::Page_HardwareDevice;
|
return WalletWizard::Page_HardwareDevice;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -51,23 +76,31 @@ int PageMenu::nextId() const {
|
||||||
bool PageMenu::validatePage() {
|
bool PageMenu::validatePage() {
|
||||||
m_fields->clearFields();
|
m_fields->clearFields();
|
||||||
|
|
||||||
if (ui->radioCreate->isChecked()) {
|
if (ui->radio_create->isChecked()) {
|
||||||
m_fields->mode = WizardMode::CreateWallet;
|
m_fields->mode = WizardMode::CreateWallet;
|
||||||
m_fields->modeText = "Create wallet";
|
m_fields->modeText = "Create wallet";
|
||||||
}
|
}
|
||||||
if (ui->radioOpen->isChecked()) {
|
if (ui->radio_createMultisig->isChecked()) {
|
||||||
|
m_fields->mode = WizardMode::CreateMultisig;
|
||||||
|
m_fields->modeText = "Create multisig wallet";
|
||||||
|
}
|
||||||
|
if (ui->radio_open->isChecked()) {
|
||||||
m_fields->mode = WizardMode::OpenWallet;
|
m_fields->mode = WizardMode::OpenWallet;
|
||||||
m_fields->modeText = "Open wallet";
|
m_fields->modeText = "Open wallet";
|
||||||
}
|
}
|
||||||
if (ui->radioSeed->isChecked()) {
|
if (ui->radio_restoreSeed->isChecked()) {
|
||||||
m_fields->mode = WizardMode::RestoreFromSeed;
|
m_fields->mode = WizardMode::RestoreFromSeed;
|
||||||
m_fields->modeText = "Restore wallet";
|
m_fields->modeText = "Restore wallet";
|
||||||
}
|
}
|
||||||
if (ui->radioViewOnly->isChecked()) {
|
if (ui->radio_restoreKeys->isChecked()) {
|
||||||
m_fields->mode = WizardMode::RestoreFromKeys;
|
m_fields->mode = WizardMode::RestoreFromKeys;
|
||||||
m_fields->modeText = "Restore wallet";
|
m_fields->modeText = "Restore wallet";
|
||||||
}
|
}
|
||||||
if (ui->radioCreateFromDevice->isChecked()) {
|
if (ui->radio_restoreMultisig->isChecked()) {
|
||||||
|
m_fields->mode = WizardMode::RestoreMultisig;
|
||||||
|
m_fields->modeText = "Restore multisig wallet";
|
||||||
|
}
|
||||||
|
if (ui->radio_restoreHardware->isChecked()) {
|
||||||
m_fields->mode = WizardMode::CreateWalletFromDevice;
|
m_fields->mode = WizardMode::CreateWalletFromDevice;
|
||||||
m_fields->modeText = "Create from hardware device";
|
m_fields->modeText = "Create from hardware device";
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ private:
|
||||||
Ui::PageMenu *ui;
|
Ui::PageMenu *ui;
|
||||||
WalletKeysFilesModel *m_walletKeysFilesModel;
|
WalletKeysFilesModel *m_walletKeysFilesModel;
|
||||||
WizardFields *m_fields;
|
WizardFields *m_fields;
|
||||||
|
|
||||||
|
WalletWizard::Page m_nextPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //FEATHER_WIZARDMENU_H
|
#endif //FEATHER_WIZARDMENU_H
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>617</width>
|
<width>610</width>
|
||||||
<height>463</height>
|
<height>465</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -15,63 +15,92 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="minimumSize">
|
<property name="title">
|
||||||
<size>
|
<string/>
|
||||||
<width>400</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Select option:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioCreate">
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::ClickFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Create new wallet</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioOpen">
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::ClickFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Open wallet file</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioSeed">
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::ClickFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Restore wallet from seed</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioViewOnly">
|
|
||||||
<property name="focusPolicy">
|
|
||||||
<enum>Qt::ClickFocus</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Restore wallet from keys</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="radioCreateFromDevice">
|
|
||||||
<property name="text">
|
|
||||||
<string>Create wallet from hardware device</string>
|
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create a new..</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_create">
|
||||||
|
<property name="text">
|
||||||
|
<string>Standard wallet</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_createMultisig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Multisig wallet</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_open">
|
||||||
|
<property name="text">
|
||||||
|
<string>Open wallet file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restore wallet from..</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_restoreSeed">
|
||||||
|
<property name="text">
|
||||||
|
<string>Seed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_restoreKeys">
|
||||||
|
<property name="text">
|
||||||
|
<string>Keys</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_restoreMultisig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Multisig seed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_restoreHardware">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hardware device</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
@ -82,24 +111,17 @@
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>20</width>
|
<width>20</width>
|
||||||
<height>40</height>
|
<height>73</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<widget class="QLabel" name="label_version">
|
||||||
<item>
|
<property name="text">
|
||||||
<widget class="QLabel" name="label_version">
|
<string>TextLabel</string>
|
||||||
<property name="enabled">
|
</property>
|
||||||
<bool>false</bool>
|
</widget>
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>by dsc & tobtoht</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
|
40
src/wizard/PageRecoverWallet.cpp
Normal file
40
src/wizard/PageRecoverWallet.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageRecoverWallet.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "WalletWizard.h"
|
||||||
|
#include "PageMenu.h"
|
||||||
|
#include "ui_PageRecoverWallet.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "config-feather.h"
|
||||||
|
|
||||||
|
PageRecoverWallet::PageRecoverWallet(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageRecoverWallet)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
this->setTitle("Recover wallet");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageRecoverWallet::initializePage() {
|
||||||
|
ui->radio_standard->setChecked(true);
|
||||||
|
this->setTitle(m_fields->modeText);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageRecoverWallet::nextId() const {
|
||||||
|
if (ui->radio_standard->isChecked())
|
||||||
|
return WalletWizard::Page_KeyType;
|
||||||
|
if (ui->radio_viewOnly->isChecked())
|
||||||
|
return WalletWizard::Page_WalletRestoreKeys;
|
||||||
|
if (ui->radio_multisig->isChecked())
|
||||||
|
return WalletWizard::Page_MultisigRestoreSeed;
|
||||||
|
if (ui->radio_hardware->isChecked())
|
||||||
|
return WalletWizard::Page_HardwareDevice;
|
||||||
|
return 0;
|
||||||
|
}
|
33
src/wizard/PageRecoverWallet.h
Normal file
33
src/wizard/PageRecoverWallet.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// Created by user on 3/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGERECOVERWALLET_H
|
||||||
|
#define FEATHER_PAGERECOVERWALLET_H
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageRecoverWallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageRecoverWallet : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageRecoverWallet(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageRecoverWallet *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGERECOVERWALLET_H
|
80
src/wizard/PageRecoverWallet.ui
Normal file
80
src/wizard/PageRecoverWallet.ui
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageRecoverWallet</class>
|
||||||
|
<widget class="QWizardPage" name="PageRecoverWallet">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>589</width>
|
||||||
|
<height>404</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Select wallet type: </string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_standard">
|
||||||
|
<property name="text">
|
||||||
|
<string>Standard</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_viewOnly">
|
||||||
|
<property name="text">
|
||||||
|
<string>View-only</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_multisig">
|
||||||
|
<property name="text">
|
||||||
|
<string>Multisig</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_hardware">
|
||||||
|
<property name="text">
|
||||||
|
<string>Hardware wallet</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -13,29 +13,42 @@ PageSetPassword::PageSetPassword(WizardFields *fields, QWidget *parent)
|
||||||
, m_fields(fields)
|
, m_fields(fields)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
this->setFinalPage(true);
|
|
||||||
|
|
||||||
ui->frame_password->setInfo(icons()->icon("lock"), "Choose a password to encrypt your wallet keys.");
|
ui->frame_password->setInfo(icons()->icon("lock"), "Choose a password to encrypt your wallet keys.");
|
||||||
|
|
||||||
connect(ui->widget_password, &PasswordSetWidget::passwordEntryChanged, [this]{
|
connect(ui->widget_password, &PasswordSetWidget::passwordEntryChanged, [this]{
|
||||||
this->completeChanged();
|
this->completeChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
this->setButtonText(QWizard::FinishButton, "Create/Open wallet");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageSetPassword::initializePage() {
|
void PageSetPassword::initializePage() {
|
||||||
|
// bool multisig = ( || m_fields->mode == WizardMode::RestoreMultisig);
|
||||||
|
this->setFinalPage(m_fields->mode != WizardMode::CreateMultisig);
|
||||||
|
// this->setFinalPage(true);
|
||||||
|
this->setButtonText(QWizard::FinishButton, "Create/Open wallet");
|
||||||
|
this->setButtonText(QWizard::CommitButton, "Next");
|
||||||
|
this->setCommitPage(true);
|
||||||
this->setTitle(m_fields->modeText);
|
this->setTitle(m_fields->modeText);
|
||||||
ui->widget_password->resetFields();
|
ui->widget_password->resetFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageSetPassword::validatePage() {
|
bool PageSetPassword::validatePage() {
|
||||||
m_fields->password = ui->widget_password->password();
|
m_fields->password = ui->widget_password->password();
|
||||||
emit createWallet();
|
|
||||||
|
// Prevent double clicks from creating a wallet twice
|
||||||
|
if (!m_walletCreated) {
|
||||||
|
emit createWallet();
|
||||||
|
m_walletCreated = true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PageSetPassword::nextId() const {
|
int PageSetPassword::nextId() const {
|
||||||
|
if (m_fields->mode == WizardMode::CreateMultisig) {
|
||||||
|
return WalletWizard::Page_MultisigCreateSetupKey;
|
||||||
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,10 +26,12 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void createWallet();
|
void createWallet();
|
||||||
|
void createMultisigWallet();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::PageSetPassword *ui;
|
Ui::PageSetPassword *ui;
|
||||||
|
|
||||||
|
bool m_walletCreated = false;
|
||||||
WizardFields *m_fields;
|
WizardFields *m_fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,9 @@ QString PageWalletFile::defaultWalletName() {
|
||||||
walletStr = QString("trezor_%1");
|
walletStr = QString("trezor_%1");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (m_fields->mode == WizardMode::CreateMultisig || m_fields->mode == WizardMode::RestoreMultisig) {
|
||||||
|
walletStr = QString("multisig_%1");
|
||||||
|
}
|
||||||
walletName = walletStr.arg(count);
|
walletName = walletStr.arg(count);
|
||||||
count++;
|
count++;
|
||||||
} while (this->walletPathExists(walletName));
|
} while (this->walletPathExists(walletName));
|
||||||
|
|
|
@ -77,6 +77,7 @@ PageWalletRestoreKeys::PageWalletRestoreKeys(WizardFields *fields, QWidget *pare
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageWalletRestoreKeys::initializePage() {
|
void PageWalletRestoreKeys::initializePage() {
|
||||||
|
ui->stackedWidget->setCurrentIndex(0);
|
||||||
this->showInputLines();
|
this->showInputLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,12 +98,18 @@ void PageWalletRestoreKeys::showInputLines() {
|
||||||
ui->frame_viewKey->hide();
|
ui->frame_viewKey->hide();
|
||||||
ui->frame_spendKey->show();
|
ui->frame_spendKey->show();
|
||||||
}
|
}
|
||||||
else {
|
else if (ui->combo_walletType->currentIndex() == walletType::Spendable_Nondeterministic){
|
||||||
ui->frame_address->show();
|
ui->frame_address->show();
|
||||||
ui->frame_viewKey->show();
|
ui->frame_viewKey->show();
|
||||||
ui->frame_spendKey->show();
|
ui->frame_spendKey->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui->combo_walletType->currentIndex() == walletType::Multisig) {
|
||||||
|
ui->stackedWidget->setCurrentIndex(1);
|
||||||
|
} else {
|
||||||
|
ui->stackedWidget->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
ui->line_address->setText("");
|
ui->line_address->setText("");
|
||||||
ui->line_viewkey->setText("");
|
ui->line_viewkey->setText("");
|
||||||
ui->line_spendkey->setText("");
|
ui->line_spendkey->setText("");
|
||||||
|
@ -111,6 +118,12 @@ void PageWalletRestoreKeys::showInputLines() {
|
||||||
bool PageWalletRestoreKeys::validatePage() {
|
bool PageWalletRestoreKeys::validatePage() {
|
||||||
auto errStyle = "QLineEdit{border: 1px solid red;}";
|
auto errStyle = "QLineEdit{border: 1px solid red;}";
|
||||||
|
|
||||||
|
if (walletType() == walletType::Multisig) {
|
||||||
|
// TODO: validation
|
||||||
|
m_fields->multisigSeed = ui->multisigSeed->toPlainText();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
ui->line_address->setStyleSheet("");
|
ui->line_address->setStyleSheet("");
|
||||||
ui->line_viewkey->setStyleSheet("");
|
ui->line_viewkey->setStyleSheet("");
|
||||||
ui->label_errorString->hide();
|
ui->label_errorString->hide();
|
||||||
|
|
|
@ -23,7 +23,8 @@ class PageWalletRestoreKeys : public QWizardPage
|
||||||
enum walletType {
|
enum walletType {
|
||||||
ViewOnly = 0,
|
ViewOnly = 0,
|
||||||
Spendable = 1,
|
Spendable = 1,
|
||||||
Spendable_Nondeterministic = 2
|
Spendable_Nondeterministic = 2,
|
||||||
|
Multisig = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -58,105 +58,167 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame_spendKey">
|
<widget class="QStackedWidget" name="stackedWidget">
|
||||||
<property name="frameShape">
|
<property name="currentIndex">
|
||||||
<enum>QFrame::NoFrame</enum>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShadow">
|
<widget class="QWidget" name="page">
|
||||||
<enum>QFrame::Raised</enum>
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
</property>
|
<property name="leftMargin">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<number>0</number>
|
||||||
<property name="leftMargin">
|
</property>
|
||||||
<number>0</number>
|
<property name="topMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="topMargin">
|
</property>
|
||||||
<number>0</number>
|
<property name="rightMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="rightMargin">
|
</property>
|
||||||
<number>0</number>
|
<property name="bottomMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="bottomMargin">
|
</property>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QFrame" name="frame_spendKey">
|
||||||
<item>
|
<property name="frameShape">
|
||||||
<widget class="QLabel" name="label_3">
|
<enum>QFrame::NoFrame</enum>
|
||||||
<property name="text">
|
</property>
|
||||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">spend</span> key</p></body></html></string>
|
<property name="frameShadow">
|
||||||
</property>
|
<enum>QFrame::Raised</enum>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
<item>
|
<property name="leftMargin">
|
||||||
<widget class="QLineEdit" name="line_spendkey"/>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
</layout>
|
<property name="topMargin">
|
||||||
</widget>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="rightMargin">
|
||||||
<widget class="QFrame" name="frame_viewKey">
|
<number>0</number>
|
||||||
<property name="frameShape">
|
</property>
|
||||||
<enum>QFrame::NoFrame</enum>
|
<property name="bottomMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="frameShadow">
|
</property>
|
||||||
<enum>QFrame::Raised</enum>
|
<item>
|
||||||
</property>
|
<widget class="QLabel" name="label_3">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
<property name="text">
|
||||||
<property name="leftMargin">
|
<string><html><head/><body><p>Secret <span style=" font-weight:600;">spend</span> key</p></body></html></string>
|
||||||
<number>0</number>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
<property name="topMargin">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QLineEdit" name="line_spendkey"/>
|
||||||
<property name="rightMargin">
|
</item>
|
||||||
<number>0</number>
|
</layout>
|
||||||
</property>
|
</widget>
|
||||||
<property name="bottomMargin">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QFrame" name="frame_viewKey">
|
||||||
<item>
|
<property name="frameShape">
|
||||||
<widget class="QLabel" name="label_viewKey">
|
<enum>QFrame::NoFrame</enum>
|
||||||
<property name="text">
|
</property>
|
||||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">view</span> key</p></body></html></string>
|
<property name="frameShadow">
|
||||||
</property>
|
<enum>QFrame::Raised</enum>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<property name="leftMargin">
|
||||||
<widget class="QLineEdit" name="line_viewkey"/>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
</layout>
|
<property name="topMargin">
|
||||||
</widget>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="rightMargin">
|
||||||
<widget class="QFrame" name="frame_address">
|
<number>0</number>
|
||||||
<property name="frameShape">
|
</property>
|
||||||
<enum>QFrame::NoFrame</enum>
|
<property name="bottomMargin">
|
||||||
</property>
|
<number>0</number>
|
||||||
<property name="frameShadow">
|
</property>
|
||||||
<enum>QFrame::Raised</enum>
|
<item>
|
||||||
</property>
|
<widget class="QLabel" name="label_viewKey">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<property name="text">
|
||||||
<property name="leftMargin">
|
<string><html><head/><body><p>Secret <span style=" font-weight:600;">view</span> key</p></body></html></string>
|
||||||
<number>0</number>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
<property name="topMargin">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QLineEdit" name="line_viewkey"/>
|
||||||
<property name="rightMargin">
|
</item>
|
||||||
<number>0</number>
|
</layout>
|
||||||
</property>
|
</widget>
|
||||||
<property name="bottomMargin">
|
</item>
|
||||||
<number>0</number>
|
<item>
|
||||||
</property>
|
<widget class="QFrame" name="frame_address">
|
||||||
<item>
|
<property name="frameShape">
|
||||||
<widget class="QLabel" name="label">
|
<enum>QFrame::NoFrame</enum>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Primary address</string>
|
<property name="frameShadow">
|
||||||
</property>
|
<enum>QFrame::Raised</enum>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<item>
|
<property name="leftMargin">
|
||||||
<widget class="QLineEdit" name="line_address"/>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
</layout>
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Primary address</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_address"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="page_multisig">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Multisig seed:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="multisigSeed"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -19,17 +19,32 @@
|
||||||
#include "PageHardwareDevice.h"
|
#include "PageHardwareDevice.h"
|
||||||
#include "PageNetworkProxy.h"
|
#include "PageNetworkProxy.h"
|
||||||
#include "PageNetworkWebsocket.h"
|
#include "PageNetworkWebsocket.h"
|
||||||
|
#include "multisig/PageMultisigExperimentalWarning.h"
|
||||||
|
#include "multisig/PageMultisigCreateSetupKey.h"
|
||||||
|
#include "multisig/PageMultisigParticipants.h"
|
||||||
|
#include "multisig/PageMultisigOwnAddress.h"
|
||||||
|
#include "multisig/PageMultisigSignerInfo.h"
|
||||||
|
#include "multisig/PageMultisigSetupDebug.h"
|
||||||
|
#include "multisig/PageMultisigSeed.h"
|
||||||
|
#include "multisig/PageMultisigEnterSetupKey.h"
|
||||||
|
#include "multisig/PageMultisigEnterChannel.h"
|
||||||
|
#include "multisig/PageMultisigSignerConfig.h"
|
||||||
|
#include "multisig/PageMultisigSetupKey.h"
|
||||||
|
#include "multisig/PageMultisigEnterName.h"
|
||||||
|
#include "multisig/PageMultisigSetupWallet.h"
|
||||||
|
#include "multisig/PageMultisigVerifyAddress.h"
|
||||||
|
#include "multisig/PageMultisigRestoreSeed.h"
|
||||||
|
#include "multisig/PageMultisigMMSRecoveryInfo.h"
|
||||||
|
#include "multisig/PageMultisigRestoreMMSRecoveryInfo.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "WindowManager.h"
|
#include "WindowManager.h"
|
||||||
|
#include "PageRecoverWallet.h"
|
||||||
#include <QLineEdit>
|
#include "PageKeyType.h"
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QScreen>
|
|
||||||
|
|
||||||
WalletWizard::WalletWizard(QWidget *parent)
|
WalletWizard::WalletWizard(QWidget *parent)
|
||||||
: QWizard(parent)
|
: QWizard(parent)
|
||||||
{
|
{
|
||||||
this->setWindowTitle("Welcome to Feather Wallet");
|
this->setWindowTitle("Feather Wizard");
|
||||||
this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
||||||
|
|
||||||
m_walletKeysFilesModel = new WalletKeysFilesModel(this);
|
m_walletKeysFilesModel = new WalletKeysFilesModel(this);
|
||||||
|
@ -45,6 +60,9 @@ WalletWizard::WalletWizard(QWidget *parent)
|
||||||
auto walletSetPasswordPage = new PageSetPassword(&m_wizardFields, this);
|
auto walletSetPasswordPage = new PageSetPassword(&m_wizardFields, this);
|
||||||
auto walletSetSeedPassphrasePage = new PageSetSeedPassphrase(&m_wizardFields, this);
|
auto walletSetSeedPassphrasePage = new PageSetSeedPassphrase(&m_wizardFields, this);
|
||||||
auto walletSetSubaddressLookaheadPage = new PageSetSubaddressLookahead(&m_wizardFields, this);
|
auto walletSetSubaddressLookaheadPage = new PageSetSubaddressLookahead(&m_wizardFields, this);
|
||||||
|
auto multisigSetupDebug = new PageMultisigSetupDebug(&m_wizardFields, this);
|
||||||
|
auto multisigSeed = new PageMultisigSeed(&m_wizardFields, this);
|
||||||
|
auto multisigRecoveryInfo = new PageMultisigMMSRecoveryInfo(&m_wizardFields, this);
|
||||||
setPage(Page_Menu, menuPage);
|
setPage(Page_Menu, menuPage);
|
||||||
setPage(Page_WalletFile, createWallet);
|
setPage(Page_WalletFile, createWallet);
|
||||||
setPage(Page_OpenWallet, openWalletPage);
|
setPage(Page_OpenWallet, openWalletPage);
|
||||||
|
@ -60,6 +78,25 @@ WalletWizard::WalletWizard(QWidget *parent)
|
||||||
setPage(Page_SetSeedPassphrase, walletSetSeedPassphrasePage);
|
setPage(Page_SetSeedPassphrase, walletSetSeedPassphrasePage);
|
||||||
setPage(Page_SetSubaddressLookahead, walletSetSubaddressLookaheadPage);
|
setPage(Page_SetSubaddressLookahead, walletSetSubaddressLookaheadPage);
|
||||||
setPage(Page_Plugins, new PagePlugins(this));
|
setPage(Page_Plugins, new PagePlugins(this));
|
||||||
|
setPage(Page_MultisigExperimentalWarning, new PageMultisigExperimentalWarning(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigCreateSetupKey, new PageMultisigCreateSetupKey(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigParticipants, new PageMultisigParticipants(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigOwnAddress, new PageMultisigOwnAddress(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigSignerInfo, new PageMultisigSignerInfo(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigSetupDebug, multisigSetupDebug);
|
||||||
|
setPage(Page_MultisigSeed, multisigSeed);
|
||||||
|
setPage(Page_MultisigEnterSetupKey, new PageMultisigEnterSetupKey(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigEnterChannel, new PageMultisigEnterChannel(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigSignerConfig, new PageMultisigSignerConfig(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigShowSetupKey, new PageMultisigSetupKey(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigEnterName, new PageMultisigEnterName(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigSetupWallet, new PageMultisigSetupWallet(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigVerifyAddress, new PageMultisigVerifyAddress(&m_wizardFields, this));
|
||||||
|
setPage(Page_Recover, new PageRecoverWallet(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigRestoreSeed, new PageMultisigRestoreSeed(&m_wizardFields, this));
|
||||||
|
setPage(Page_KeyType, new PageKeyType(&m_wizardFields, this));
|
||||||
|
setPage(Page_MultisigMMSRecoveryInfo, multisigRecoveryInfo);
|
||||||
|
setPage(Page_MultisigRestoreMMSRecoveryInfo, new PageMultisigRestoreMMSRecoveryInfo(&m_wizardFields, this));
|
||||||
|
|
||||||
setStartId(Page_Menu);
|
setStartId(Page_Menu);
|
||||||
|
|
||||||
|
@ -80,6 +117,8 @@ WalletWizard::WalletWizard(QWidget *parent)
|
||||||
layout << QWizard::CommitButton;
|
layout << QWizard::CommitButton;
|
||||||
this->setButtonLayout(layout);
|
this->setButtonLayout(layout);
|
||||||
|
|
||||||
|
this->setButtonText(QWizard::CommitButton, "Next");
|
||||||
|
|
||||||
auto *settingsButton = new QPushButton("Settings", this);
|
auto *settingsButton = new QPushButton("Settings", this);
|
||||||
this->setButton(QWizard::CustomButton1, settingsButton);
|
this->setButton(QWizard::CustomButton1, settingsButton);
|
||||||
|
|
||||||
|
@ -102,6 +141,11 @@ WalletWizard::WalletWizard(QWidget *parent)
|
||||||
emit openWallet(path, "");
|
emit openWallet(path, "");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(multisigRecoveryInfo, &PageMultisigMMSRecoveryInfo::showWallet, [this](Wallet* wallet){
|
||||||
|
m_wizardFields.wallet = nullptr;
|
||||||
|
emit showWallet(wallet);
|
||||||
|
});
|
||||||
|
|
||||||
connect(this, &QWizard::helpRequested, this, &WalletWizard::showHelp);
|
connect(this, &QWizard::helpRequested, this, &WalletWizard::showHelp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +153,10 @@ void WalletWizard::resetFields() {
|
||||||
m_wizardFields = {};
|
m_wizardFields = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WalletWizard::setWallet(Wallet *wallet) {
|
||||||
|
m_wizardFields.wallet = wallet;
|
||||||
|
}
|
||||||
|
|
||||||
void WalletWizard::onCreateWallet() {
|
void WalletWizard::onCreateWallet() {
|
||||||
auto walletPath = QString("%1/%2").arg(m_wizardFields.walletDir, m_wizardFields.walletName);
|
auto walletPath = QString("%1/%2").arg(m_wizardFields.walletDir, m_wizardFields.walletName);
|
||||||
|
|
||||||
|
@ -147,6 +195,21 @@ void WalletWizard::onCreateWallet() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_wizardFields.mode == WizardMode::CreateMultisig) {
|
||||||
|
// We didn't generate a seed, generate one here.
|
||||||
|
m_wizardFields.seed = Seed(Seed::Type::POLYSEED, constants::networkType, "English", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_wizardFields.mode == WizardMode::RestoreMultisig) {
|
||||||
|
emit restoreMultisigWallet(walletPath,
|
||||||
|
m_wizardFields.password,
|
||||||
|
m_wizardFields.multisigSeed,
|
||||||
|
m_wizardFields.multisigMMSRecovery,
|
||||||
|
m_wizardFields.restoreHeight,
|
||||||
|
m_wizardFields.subaddressLookahead);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If we're connected to the websocket, use the reported height for new wallets to skip initial synchronization.
|
// If we're connected to the websocket, use the reported height for new wallets to skip initial synchronization.
|
||||||
if (m_wizardFields.mode == WizardMode::CreateWallet && currentBlockHeight > 0) {
|
if (m_wizardFields.mode == WizardMode::CreateWallet && currentBlockHeight > 0) {
|
||||||
qInfo() << "New wallet, setting restore height to latest blockheight: " << currentBlockHeight;
|
qInfo() << "New wallet, setting restore height to latest blockheight: " << currentBlockHeight;
|
||||||
|
@ -157,9 +220,10 @@ void WalletWizard::onCreateWallet() {
|
||||||
m_wizardFields.seed.setRestoreHeight(m_wizardFields.restoreHeight);
|
m_wizardFields.seed.setRestoreHeight(m_wizardFields.restoreHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool newWallet = m_wizardFields.mode == WizardMode::CreateWallet;
|
bool newWallet = (m_wizardFields.mode == WizardMode::CreateWallet || m_wizardFields.mode == WizardMode::CreateMultisig);
|
||||||
|
|
||||||
emit createWallet(m_wizardFields.seed, walletPath, m_wizardFields.password, m_wizardFields.seedLanguage, m_wizardFields.seedOffsetPassphrase, m_wizardFields.subaddressLookahead, newWallet);
|
bool giveToWizard = m_wizardFields.mode == WizardMode::CreateMultisig;
|
||||||
|
emit createWallet(m_wizardFields.seed, walletPath, m_wizardFields.password, m_wizardFields.seedLanguage, m_wizardFields.seedOffsetPassphrase, m_wizardFields.subaddressLookahead, newWallet, giveToWizard);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString WalletWizard::helpPage() {
|
QString WalletWizard::helpPage() {
|
||||||
|
@ -195,4 +259,8 @@ void WalletWizard::showHelp() {
|
||||||
if (!doc.isEmpty()) {
|
if (!doc.isEmpty()) {
|
||||||
windowManager()->showDocs(this, doc);
|
windowManager()->showDocs(this, doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletWizard::~WalletWizard() {
|
||||||
|
delete m_wizardFields.wallet;
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@ enum WizardMode {
|
||||||
OpenWallet,
|
OpenWallet,
|
||||||
RestoreFromSeed,
|
RestoreFromSeed,
|
||||||
RestoreFromKeys,
|
RestoreFromKeys,
|
||||||
CreateWalletFromDevice
|
CreateWalletFromDevice,
|
||||||
|
CreateMultisig,
|
||||||
|
RestoreMultisig
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DeviceType {
|
enum DeviceType {
|
||||||
|
@ -27,6 +29,18 @@ enum DeviceType {
|
||||||
TREZOR
|
TREZOR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SignerConfig {
|
||||||
|
AUTOMATIC = 0,
|
||||||
|
SEMI_AUTOMATIC,
|
||||||
|
MANUAL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MMSSigner {
|
||||||
|
quint32 index = 0;
|
||||||
|
QString label;
|
||||||
|
QString address;
|
||||||
|
};
|
||||||
|
|
||||||
struct WizardFields {
|
struct WizardFields {
|
||||||
QString walletName;
|
QString walletName;
|
||||||
QString walletDir;
|
QString walletDir;
|
||||||
|
@ -46,6 +60,17 @@ struct WizardFields {
|
||||||
Seed::Type seedType;
|
Seed::Type seedType;
|
||||||
DeviceType deviceType;
|
DeviceType deviceType;
|
||||||
QString subaddressLookahead;
|
QString subaddressLookahead;
|
||||||
|
bool multisigInitiator = false;
|
||||||
|
QString multisigSetupKey;
|
||||||
|
quint32 multisigThreshold = 0;
|
||||||
|
quint32 multisigSigners = 0;
|
||||||
|
bool multisigAutomaticSetup = true;
|
||||||
|
QString multisigUsername;
|
||||||
|
QString multisigService;
|
||||||
|
QString multisigChannel;
|
||||||
|
QString multisigSeed;
|
||||||
|
QString multisigMMSRecovery;
|
||||||
|
Wallet *wallet = nullptr;
|
||||||
|
|
||||||
void clearFields() {
|
void clearFields() {
|
||||||
showSetSeedPassphrasePage = false;
|
showSetSeedPassphrasePage = false;
|
||||||
|
@ -58,6 +83,7 @@ struct WizardFields {
|
||||||
secretSpendKey = "";
|
secretSpendKey = "";
|
||||||
restoreHeight = 0;
|
restoreHeight = 0;
|
||||||
subaddressLookahead = "";
|
subaddressLookahead = "";
|
||||||
|
wallet = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
WizardFields(): deviceType(DeviceType::LEDGER), mode(WizardMode::CreateWallet),
|
WizardFields(): deviceType(DeviceType::LEDGER), mode(WizardMode::CreateWallet),
|
||||||
|
@ -78,26 +104,50 @@ public:
|
||||||
Page_SetSubaddressLookahead,
|
Page_SetSubaddressLookahead,
|
||||||
Page_OpenWallet,
|
Page_OpenWallet,
|
||||||
Page_Network,
|
Page_Network,
|
||||||
|
Page_Recover,
|
||||||
Page_WalletRestoreSeed,
|
Page_WalletRestoreSeed,
|
||||||
Page_WalletRestoreKeys,
|
Page_WalletRestoreKeys,
|
||||||
Page_SetRestoreHeight,
|
Page_SetRestoreHeight,
|
||||||
Page_HardwareDevice,
|
Page_HardwareDevice,
|
||||||
Page_NetworkProxy,
|
Page_NetworkProxy,
|
||||||
Page_NetworkWebsocket,
|
Page_NetworkWebsocket,
|
||||||
Page_Plugins
|
Page_Plugins,
|
||||||
|
Page_MultisigExperimentalWarning,
|
||||||
|
Page_MultisigCreateSetupKey,
|
||||||
|
Page_MultisigParticipants,
|
||||||
|
Page_MultisigOwnAddress,
|
||||||
|
Page_MultisigSignerInfo,
|
||||||
|
Page_MultisigSetupDebug,
|
||||||
|
Page_MultisigSeed,
|
||||||
|
Page_MultisigEnterSetupKey,
|
||||||
|
Page_MultisigEnterChannel,
|
||||||
|
Page_MultisigSignerConfig,
|
||||||
|
Page_MultisigShowSetupKey,
|
||||||
|
Page_MultisigEnterName,
|
||||||
|
Page_MultisigSetupWallet,
|
||||||
|
Page_MultisigVerifyAddress,
|
||||||
|
Page_MultisigRestoreSeed,
|
||||||
|
Page_MultisigMMSRecoveryInfo,
|
||||||
|
Page_MultisigRestoreMMSRecoveryInfo,
|
||||||
|
Page_KeyType
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit WalletWizard(QWidget *parent = nullptr);
|
explicit WalletWizard(QWidget *parent = nullptr);
|
||||||
|
~WalletWizard() override;
|
||||||
|
|
||||||
void resetFields();
|
void resetFields();
|
||||||
|
void setWallet(Wallet* wallet);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void initialNetworkConfigured();
|
void initialNetworkConfigured();
|
||||||
void showSettings();
|
void showSettings();
|
||||||
void openWallet(QString path, QString password);
|
void openWallet(QString path, QString password);
|
||||||
|
void showWallet(Wallet *wallet);
|
||||||
|
|
||||||
void createWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight, const QString &subaddressLookahead);
|
void createWalletFromDevice(const QString &path, const QString &password, const QString &deviceName, int restoreHeight, const QString &subaddressLookahead);
|
||||||
void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, const QString subaddressLookahead = "");
|
void createWalletFromKeys(const QString &path, const QString &password, const QString &address, const QString &viewkey, const QString &spendkey, quint64 restoreHeight, const QString subaddressLookahead = "");
|
||||||
void createWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage, const QString &seedOffset, const QString &subaddressLookahead, bool newWallet);
|
void createWallet(Seed seed, const QString &path, const QString &password, const QString &seedLanguage, const QString &seedOffset, const QString &subaddressLookahead, bool newWallet, bool giveToWizard);
|
||||||
|
void restoreMultisigWallet(const QString &path, const QString &password, const QString &multisigSeed, const QString &mmsRecovery, quint64 restoreHeight, const QString &subaddressLookahead);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onCreateWallet();
|
void onCreateWallet();
|
||||||
|
|
54
src/wizard/multisig/PageMultisigCreateSetupKey.cpp
Normal file
54
src/wizard/multisig/PageMultisigCreateSetupKey.cpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigCreateSetupKey.h"
|
||||||
|
#include "ui_PageMultisigCreateSetupKey.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
#include "string_tools.h"
|
||||||
|
|
||||||
|
PageMultisigCreateSetupKey::PageMultisigCreateSetupKey(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigCreateSetupKey)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Create or use setup key");
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("key.png"), "One cosigner creates a new setup key and shares it with other "
|
||||||
|
"cosigners over a secure channel (e.g. end-to-end encrypted group chat.)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigCreateSetupKey::initializePage() {
|
||||||
|
// We may need to return to the wizard if it is closed before the multisig setup completes
|
||||||
|
// Set a cache attribute to indicate that the setup is in progress
|
||||||
|
m_fields->wallet->setCacheAttribute("feather.multisig_setup", "started");
|
||||||
|
|
||||||
|
m_fields->mode = WizardMode::CreateMultisig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int PageMultisigCreateSetupKey::nextId() const {
|
||||||
|
if (ui->radio_createSetupKey->isChecked()) {
|
||||||
|
// We are creating a new setup key
|
||||||
|
return WalletWizard::Page_MultisigParticipants;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We already have a setup key
|
||||||
|
return WalletWizard::Page_MultisigEnterSetupKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigCreateSetupKey::validatePage() {
|
||||||
|
// Prevent double click on the previous page from going to the next page
|
||||||
|
if (!ui->radio_haveSetupKey->isChecked() && !ui->radio_createSetupKey->isChecked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fields->multisigInitiator = ui->radio_createSetupKey->isChecked();
|
||||||
|
return true;
|
||||||
|
}
|
31
src/wizard/multisig/PageMultisigCreateSetupKey.h
Normal file
31
src/wizard/multisig/PageMultisigCreateSetupKey.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEMULTISIGCREATESETUPKEY_H
|
||||||
|
#define FEATHER_PAGEMULTISIGCREATESETUPKEY_H
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigCreateSetupKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigCreateSetupKey : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigCreateSetupKey(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
[[nodiscard]] int nextId() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageMultisigCreateSetupKey *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEMULTISIGCREATESETUPKEY_H
|
82
src/wizard/multisig/PageMultisigCreateSetupKey.ui
Normal file
82
src/wizard/multisig/PageMultisigCreateSetupKey.ui
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigCreateSetupKey</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigCreateSetupKey">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>603</width>
|
||||||
|
<height>536</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="infoFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_haveSetupKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>I have a setup key</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_createSetupKey">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create a new setup key</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
121
src/wizard/multisig/PageMultisigEnterChannel.cpp
Normal file
121
src/wizard/multisig/PageMultisigEnterChannel.cpp
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigEnterChannel.h"
|
||||||
|
#include "ui_PageMultisigEnterChannel.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
#include "ringct/rctOps.h"
|
||||||
|
#include "string_tools.h"
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
|
||||||
|
PageMultisigEnterChannel::PageMultisigEnterChannel(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigEnterChannel)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Create setup key (2/3)");
|
||||||
|
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("mail.png"), "Enter the URL for the messaging service that all participants will use.\n\nMake sure everyone agrees to use this service.");
|
||||||
|
|
||||||
|
connect(ui->check_authRequired, &QCheckBox::toggled, [this](bool checked){
|
||||||
|
ui->frame_auth->setVisible(checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->line_service, &QLineEdit::textChanged, [this] {
|
||||||
|
m_channelRegistered = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->radio_no, &QRadioButton::toggled, [this] {
|
||||||
|
completeChanged();
|
||||||
|
});
|
||||||
|
connect(ui->radio_yes, &QRadioButton::toggled, [this] {
|
||||||
|
completeChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigEnterChannel::registerChannel() {
|
||||||
|
QString serviceUrl = ui->line_service->text();
|
||||||
|
QString serviceLogin = ui->check_authRequired->isChecked() ? ui->line_apiKey->text() : "";
|
||||||
|
|
||||||
|
m_fields->wallet->mmsStore()->setServiceDetails(serviceUrl, serviceLogin);
|
||||||
|
|
||||||
|
// TODO: make async
|
||||||
|
QString channel;
|
||||||
|
bool success = m_fields->wallet->mmsStore()->registerChannel(channel, m_fields->multisigSigners);
|
||||||
|
if (success) {
|
||||||
|
m_fields->multisigChannel = channel;
|
||||||
|
m_channelRegistered = true;
|
||||||
|
completeChanged();
|
||||||
|
} else {
|
||||||
|
QString errorString = m_fields->wallet->mmsStore()->errorString();
|
||||||
|
|
||||||
|
if (errorString.contains("authentication")) {
|
||||||
|
ui->check_authRequired->setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::showError(this, "Unable to register channel", m_fields->wallet->mmsStore()->errorString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigEnterChannel::initializePage() {
|
||||||
|
ui->frame_auth->hide();
|
||||||
|
ui->frame_confirm->hide();
|
||||||
|
ui->check_authRequired->setChecked(false);
|
||||||
|
|
||||||
|
if (!m_fields->multisigInitiator) {
|
||||||
|
this->setTitle("Messaging service");
|
||||||
|
ui->check_authRequired->hide();
|
||||||
|
ui->line_service->setText(m_fields->multisigService);
|
||||||
|
ui->line_service->setReadOnly(true);
|
||||||
|
ui->infoFrame->setText("The initiator has chosen the following messaging service.");
|
||||||
|
ui->frame_confirm->show();
|
||||||
|
|
||||||
|
m_channelRegistered = true;
|
||||||
|
completeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigEnterChannel::nextId() const {
|
||||||
|
if (m_fields->mode == WizardMode::CreateMultisig) {
|
||||||
|
if (m_fields->multisigInitiator) {
|
||||||
|
return WalletWizard::Page_MultisigSignerConfig;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return WalletWizard::Page_MultisigEnterName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_fields->mode == WizardMode::RestoreMultisig) {
|
||||||
|
return WalletWizard::Page_SetRestoreHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigEnterChannel::validatePage() {
|
||||||
|
if (!m_channelRegistered) {
|
||||||
|
this->registerChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_channelRegistered) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fields->multisigService = ui->line_service->text();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigEnterChannel::isComplete() const {
|
||||||
|
if (!m_fields->multisigInitiator) {
|
||||||
|
return ui->radio_yes->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
40
src/wizard/multisig/PageMultisigEnterChannel.h
Normal file
40
src/wizard/multisig/PageMultisigEnterChannel.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEMULTISIGENTERCHANNEL_H
|
||||||
|
#define FEATHER_PAGEMULTISIGENTERCHANNEL_H
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigEnterChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigEnterChannel : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigEnterChannel(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void registerChannel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageMultisigEnterChannel *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
|
||||||
|
bool m_channelRegistered = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEMULTISIGENTERCHANNEL_H
|
147
src/wizard/multisig/PageMultisigEnterChannel.ui
Normal file
147
src/wizard/multisig/PageMultisigEnterChannel.ui
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigEnterChannel</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigEnterChannel">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>561</width>
|
||||||
|
<height>310</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="infoFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Messaging service:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_service">
|
||||||
|
<property name="text">
|
||||||
|
<string>http://127.0.0.1:8442/</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>5</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="check_authRequired">
|
||||||
|
<property name="text">
|
||||||
|
<string>This service requires authentication</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_auth">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_key">
|
||||||
|
<property name="text">
|
||||||
|
<string>API key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_apiKey"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_confirm">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Is this ok?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_no">
|
||||||
|
<property name="text">
|
||||||
|
<string>No</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_yes">
|
||||||
|
<property name="text">
|
||||||
|
<string>Yes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
57
src/wizard/multisig/PageMultisigEnterName.cpp
Normal file
57
src/wizard/multisig/PageMultisigEnterName.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigEnterName.h"
|
||||||
|
#include "ui_PageMultisigEnterName.h"
|
||||||
|
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
|
||||||
|
PageMultisigEnterName::PageMultisigEnterName(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigEnterName)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Enter your name");
|
||||||
|
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("change_account.png"), "Enter your (user)name, so other participants "
|
||||||
|
"can identify you.\n\nYour name will not become known "
|
||||||
|
"to the messaging service.");
|
||||||
|
|
||||||
|
// We can't change the service URL or username after the setup has started.
|
||||||
|
this->setCommitPage(true);
|
||||||
|
this->setButtonText(QWizard::CommitButton, "Next");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigEnterName::initializePage() {
|
||||||
|
ui->line_name->setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigEnterName::nextId() const {
|
||||||
|
if (m_fields->multisigAutomaticSetup) {
|
||||||
|
return WalletWizard::Page_MultisigSetupWallet;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return WalletWizard::Page_MultisigOwnAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigEnterName::validatePage() {
|
||||||
|
QString username = ui->line_name->text();
|
||||||
|
if (username.isEmpty()) {
|
||||||
|
Utils::showError(this, "Enter a name to continue");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fields->multisigUsername = username;
|
||||||
|
|
||||||
|
// We now have all the information needed to init the MMS
|
||||||
|
if (m_fields->mode == WizardMode::CreateMultisig) {
|
||||||
|
m_fields->wallet->mmsStore()->init(m_fields->multisigSetupKey, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
30
src/wizard/multisig/PageMultisigEnterName.h
Normal file
30
src/wizard/multisig/PageMultisigEnterName.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef PAGEMULTISIGENTERNAME_H
|
||||||
|
#define PAGEMULTISIGENTERNAME_H
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigEnterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigEnterName : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigEnterName(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageMultisigEnterName *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //PAGEMULTISIGENTERNAME_H
|
62
src/wizard/multisig/PageMultisigEnterName.ui
Normal file
62
src/wizard/multisig/PageMultisigEnterName.ui
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigEnterName</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigEnterName">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>646</width>
|
||||||
|
<height>477</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="infoFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Your name: </string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_name"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
74
src/wizard/multisig/PageMultisigEnterSetupKey.cpp
Normal file
74
src/wizard/multisig/PageMultisigEnterSetupKey.cpp
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigEnterSetupKey.h"
|
||||||
|
#include "ui_PageMultisigEnterSetupKey.h"
|
||||||
|
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
|
PageMultisigEnterSetupKey::PageMultisigEnterSetupKey(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigEnterSetupKey)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Enter setup key");
|
||||||
|
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("key.png"), "The setup key should not be shared with outsiders and only used once per participant.");
|
||||||
|
|
||||||
|
connect(ui->line_setupKey, &QLineEdit::textChanged, this, &PageMultisigEnterSetupKey::checkSetupKey);
|
||||||
|
connect(ui->radio_yes, &QRadioButton::toggled, [this](bool toggled){
|
||||||
|
completeChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigEnterSetupKey::checkSetupKey(const QString &setupKey) {
|
||||||
|
ui->frame_invalid->hide();
|
||||||
|
ui->frame_verify->hide();
|
||||||
|
|
||||||
|
if (setupKey.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultisigMessageStore::SetupKey key;
|
||||||
|
bool keyValid = m_fields->wallet->mmsStore()->checkSetupKey(setupKey, key);
|
||||||
|
|
||||||
|
if (!keyValid) {
|
||||||
|
ui->frame_invalid->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fields->multisigThreshold = key.threshold;
|
||||||
|
m_fields->multisigSigners = key.participants;
|
||||||
|
m_fields->multisigService = key.service;
|
||||||
|
m_fields->multisigAutomaticSetup = (key.mode == MultisigMessageStore::SetupMode::AUTOMATIC);
|
||||||
|
m_fields->multisigSetupKey = setupKey;
|
||||||
|
|
||||||
|
ui->frame_verify->show();
|
||||||
|
ui->label_verify->setText(QString("You are setting up a %1-of-%2 multisig wallet. Is that correct?").arg(QString::number(key.threshold), QString::number(key.participants)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigEnterSetupKey::initializePage() {
|
||||||
|
ui->frame_invalid->hide();
|
||||||
|
ui->frame_verify->hide();
|
||||||
|
ui->line_setupKey->setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigEnterSetupKey::nextId() const {
|
||||||
|
return WalletWizard::Page_MultisigEnterChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigEnterSetupKey::validatePage() {
|
||||||
|
m_fields->multisigSetupKey = ui->line_setupKey->text();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigEnterSetupKey::isComplete() const {
|
||||||
|
if (ui->radio_yes->isChecked()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
35
src/wizard/multisig/PageMultisigEnterSetupKey.h
Normal file
35
src/wizard/multisig/PageMultisigEnterSetupKey.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEMULTISIGENTERSETUPKEY_H
|
||||||
|
#define FEATHER_PAGEMULTISIGENTERSETUPKEY_H
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigEnterSetupKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigEnterSetupKey : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigEnterSetupKey(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void checkSetupKey(const QString &setupKey);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageMultisigEnterSetupKey *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEMULTISIGENTERSETUPKEY_H
|
120
src/wizard/multisig/PageMultisigEnterSetupKey.ui
Normal file
120
src/wizard/multisig/PageMultisigEnterSetupKey.ui
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigEnterSetupKey</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigEnterSetupKey">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>664</width>
|
||||||
|
<height>510</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="infoFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Setup key:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_setupKey"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_invalid">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Invalid setup key entered.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_verify">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_verify">
|
||||||
|
<property name="text">
|
||||||
|
<string>You are about to create a n-of-m multisig wallet. Is that correct?</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_no">
|
||||||
|
<property name="text">
|
||||||
|
<string>No</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_yes">
|
||||||
|
<property name="text">
|
||||||
|
<string>Yes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
51
src/wizard/multisig/PageMultisigExperimentalWarning.cpp
Normal file
51
src/wizard/multisig/PageMultisigExperimentalWarning.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigExperimentalWarning.h"
|
||||||
|
#include "ui_PageMultisigExperimentalWarning.h"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
|
||||||
|
PageMultisigExperimentalWarning::PageMultisigExperimentalWarning(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigExperimentalWarning)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Experimental Feature Warning");
|
||||||
|
|
||||||
|
ui->warningFrame->setInfo(icons()->icon("warning.png"), "This is an experimental feature.");
|
||||||
|
|
||||||
|
ui->label_warning->setText("Monero's multisig implementation has not been audited.<br><br>"
|
||||||
|
"It might be fundamentally broken, which could result in a <u>loss of funds</u>.<br><br>"
|
||||||
|
"You are strongly advised to only test this feature with cosigners you trust.");
|
||||||
|
ui->label_warning->setTextFormat(Qt::RichText);
|
||||||
|
|
||||||
|
connect(ui->check_confirm, &QCheckBox::clicked, [this](bool checked){
|
||||||
|
completeChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigExperimentalWarning::initializePage() {
|
||||||
|
ui->check_confirm->setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigExperimentalWarning::nextId() const {
|
||||||
|
return WalletWizard::Page_WalletFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigExperimentalWarning::validatePage() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigExperimentalWarning::isComplete() const {
|
||||||
|
if (ui->check_confirm->isChecked()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
31
src/wizard/multisig/PageMultisigExperimentalWarning.h
Normal file
31
src/wizard/multisig/PageMultisigExperimentalWarning.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEMULTISIGEXPERIMENTALWARNING_H
|
||||||
|
#define FEATHER_PAGEMULTISIGEXPERIMENTALWARNING_H
|
||||||
|
|
||||||
|
#include <QWizardPage>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigExperimentalWarning;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigExperimentalWarning : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigExperimentalWarning(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
Ui::PageMultisigExperimentalWarning *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEMULTISIGEXPERIMENTALWARNING_H
|
97
src/wizard/multisig/PageMultisigExperimentalWarning.ui
Normal file
97
src/wizard/multisig/PageMultisigExperimentalWarning.ui
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigExperimentalWarning</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigExperimentalWarning">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>497</width>
|
||||||
|
<height>330</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="warningFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_warning">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="check_confirm">
|
||||||
|
<property name="text">
|
||||||
|
<string>I understand.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
73
src/wizard/multisig/PageMultisigMMSRecoveryInfo.cpp
Normal file
73
src/wizard/multisig/PageMultisigMMSRecoveryInfo.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigMMSRecoveryInfo.h"
|
||||||
|
#include "ui_PageMultisigMMSRecoveryInfo.h"
|
||||||
|
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
#include "libwalletqt/MultisigMessageStore.h"
|
||||||
|
|
||||||
|
PageMultisigMMSRecoveryInfo::PageMultisigMMSRecoveryInfo(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigMMSRecoveryInfo)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("MMS Recovery Info");
|
||||||
|
|
||||||
|
this->setFinalPage(true);
|
||||||
|
this->setButtonText(QWizard::FinishButton, "Open wallet");
|
||||||
|
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("key.png"), "You will need this recovery info to reconnect to the messaging service if you need to restore your wallet.\n\nStore it safely alongside your seed.");
|
||||||
|
|
||||||
|
connect(ui->check_saved, &QCheckBox::toggled, [this]{
|
||||||
|
this->completeChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigMMSRecoveryInfo::initializePage() {
|
||||||
|
// QJsonDocument doc;
|
||||||
|
// QJsonObject obj;
|
||||||
|
// obj["restore_height"] = QString::number(m_fields->wallet->getWalletCreationHeight()).toInt();
|
||||||
|
// obj["message_daemon"] = m_fields->multisigService;
|
||||||
|
// obj["setup_key"] = m_fields->multisigSetupKey;
|
||||||
|
//
|
||||||
|
// QJsonObject me;
|
||||||
|
// me["address"] = m_fields->originalPrimaryAddress;
|
||||||
|
// me["viewkey"] = m_fields->originalSecretViewKey;
|
||||||
|
// me["label"] = m_fields->multisigUsername;
|
||||||
|
//
|
||||||
|
// QJsonArray signers;
|
||||||
|
// signers.append(me);
|
||||||
|
//
|
||||||
|
// for (int i = 1; i < m_fields->wallet->multisigSigners(); i++) {
|
||||||
|
// QJsonObject signer;
|
||||||
|
// auto info = m_fields->wallet->mmsStore()->getSignerInfo(i);
|
||||||
|
//
|
||||||
|
// signer["address"] = info.address;
|
||||||
|
// signer["label"] = info.label;
|
||||||
|
//
|
||||||
|
// signers.append(signer);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// obj["signers"] = signers;
|
||||||
|
// doc.setObject(obj);
|
||||||
|
//
|
||||||
|
// QString recoveryData = QString("MMS_RECOVERY:%1").arg(doc.toJson(QJsonDocument::Compact).toBase64());
|
||||||
|
ui->mmsRecoveryInfo->setPlainText(m_fields->wallet->mmsStore()->getRecoveryInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigMMSRecoveryInfo::nextId() const {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigMMSRecoveryInfo::validatePage() {
|
||||||
|
emit showWallet(m_fields->wallet);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PageMultisigMMSRecoveryInfo::isComplete() const {
|
||||||
|
return ui->check_saved->isChecked();
|
||||||
|
}
|
37
src/wizard/multisig/PageMultisigMMSRecoveryInfo.h
Normal file
37
src/wizard/multisig/PageMultisigMMSRecoveryInfo.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#ifndef FEATHER_PAGEMULTISIGMMSRECOVERYINFO_H
|
||||||
|
#define FEATHER_PAGEMULTISIGMMSRECOVERYINFO_H
|
||||||
|
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QWizardPage>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "wizard/WalletWizard.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class PageMultisigMMSRecoveryInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PageMultisigMMSRecoveryInfo : public QWizardPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PageMultisigMMSRecoveryInfo(WizardFields *fields, QWidget *parent = nullptr);
|
||||||
|
void initializePage() override;
|
||||||
|
bool validatePage() override;
|
||||||
|
int nextId() const override;
|
||||||
|
bool isComplete() const override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void showWallet(Wallet *wallet);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::PageMultisigMMSRecoveryInfo *ui;
|
||||||
|
WizardFields *m_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_PAGEMULTISIGMMSRECOVERYINFO_H
|
80
src/wizard/multisig/PageMultisigMMSRecoveryInfo.ui
Normal file
80
src/wizard/multisig/PageMultisigMMSRecoveryInfo.ui
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>PageMultisigMMSRecoveryInfo</class>
|
||||||
|
<widget class="QWizardPage" name="PageMultisigMMSRecoveryInfo">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>648</width>
|
||||||
|
<height>570</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>WizardPage</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="InfoFrame" name="infoFrame">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPlainTextEdit" name="mmsRecoveryInfo"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_saveToFile">
|
||||||
|
<property name="text">
|
||||||
|
<string>Save to file</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_copy">
|
||||||
|
<property name="text">
|
||||||
|
<string>Copy</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="check_saved">
|
||||||
|
<property name="text">
|
||||||
|
<string>I have saved my multisig seed and MMS recovery info.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>InfoFrame</class>
|
||||||
|
<extends>QFrame</extends>
|
||||||
|
<header>components.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
31
src/wizard/multisig/PageMultisigOwnAddress.cpp
Normal file
31
src/wizard/multisig/PageMultisigOwnAddress.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2024 The Monero Project
|
||||||
|
|
||||||
|
#include "PageMultisigOwnAddress.h"
|
||||||
|
#include "ui_PageMultisigOwnAddress.h"
|
||||||
|
|
||||||
|
#include "utils/Icons.h"
|
||||||
|
#include "utils/Utils.h"
|
||||||
|
|
||||||
|
PageMultisigOwnAddress::PageMultisigOwnAddress(WizardFields *fields, QWidget *parent)
|
||||||
|
: QWizardPage(parent)
|
||||||
|
, ui(new Ui::PageMultisigOwnAddress)
|
||||||
|
, m_fields(fields)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
this->setTitle("Your wallet address");
|
||||||
|
|
||||||
|
ui->infoFrame->setInfo(icons()->icon("tab_addresses.png"), "Copy the address below and send it to all co-signers.\n\nThe address is used to encrypt messages.");
|
||||||
|
|
||||||
|
connect(ui->btn_copyAddress, &QPushButton::clicked, [this]{
|
||||||
|
Utils::copyToClipboard(m_fields->wallet->address(0, 0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PageMultisigOwnAddress::initializePage() {
|
||||||
|
ui->line_ownAddress->setPlainText(m_fields->wallet->address(0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int PageMultisigOwnAddress::nextId() const {
|
||||||
|
return WalletWizard::Page_MultisigSignerInfo;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue