mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 03:29:24 +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()
|
||||
|
||||
if(STATIC)
|
||||
# add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||
# add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||
if(MINGW)
|
||||
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()
|
||||
|
||||
|
|
2
monero
2
monero
|
@ -1 +1 @@
|
|||
Subproject commit 892a9a16d169ecd8f15e892f1e14fdd84cfce0f3
|
||||
Subproject commit f82b6889af63e7d0fbed0b3f7032f0c2924cf812
|
|
@ -20,8 +20,8 @@ endif()
|
|||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ${QT_COMPONENTS})
|
||||
|
||||
set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
add_subdirectory(third-party/singleapplication)
|
||||
#set(QAPPLICATION_CLASS QApplication CACHE STRING "Inheritance class for SingleApplication")
|
||||
#add_subdirectory(third-party/singleapplication)
|
||||
|
||||
if (CHECK_UPDATES)
|
||||
add_subdirectory(openpgp)
|
||||
|
@ -55,6 +55,8 @@ file(GLOB SOURCE_FILES
|
|||
"widgets/*.cpp"
|
||||
"wizard/*.h"
|
||||
"wizard/*.cpp"
|
||||
"wizard/multisig/*.h"
|
||||
"wizard/multisig/*.cpp"
|
||||
"wallet/*.h"
|
||||
"wallet/*.cpp"
|
||||
"qrcode/*.h"
|
||||
|
@ -272,7 +274,7 @@ target_link_libraries(feather PRIVATE
|
|||
Threads::Threads
|
||||
${QRENCODE_LIBRARY}
|
||||
${POLYSEED_LIBRARY}
|
||||
SingleApplication::SingleApplication
|
||||
# SingleApplication::SingleApplication
|
||||
${ICU_LIBRARIES}
|
||||
${LIBZIP_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
|
|
|
@ -89,11 +89,8 @@ void CoinsWidget::setModel(CoinsModel * model, Coins * coins) {
|
|||
ui->coins->setColumnHidden(CoinsModel::SpentHeight, true);
|
||||
ui->coins->setColumnHidden(CoinsModel::Frozen, true);
|
||||
|
||||
if (!m_wallet->viewOnly()) {
|
||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, true);
|
||||
} else {
|
||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, false);
|
||||
}
|
||||
bool showKeyImageKnown = m_wallet->viewOnly() || m_wallet->isMultisig();
|
||||
ui->coins->setColumnHidden(CoinsModel::KeyImageKnown, !showKeyImageKnown);
|
||||
|
||||
ui->coins->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
ui->coins->header()->setSectionResizeMode(CoinsModel::Label, QHeaderView::Stretch);
|
||||
|
@ -335,6 +332,11 @@ bool CoinsWidget::isCoinSpendable(CoinsInfo *coin) {
|
|||
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()) {
|
||||
Utils::showError(this, "Unable to spend outputs", "Selected output was already spent");
|
||||
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);
|
||||
ui->coinsWidgetLayout->addWidget(m_coinsWidget);
|
||||
|
||||
// [MMS]
|
||||
m_mmsWidget = new MMSWidget(m_wallet, this);
|
||||
ui->mmsWidgetLayout->addWidget(m_mmsWidget);
|
||||
|
||||
// [Plugins..]
|
||||
for (auto* plugin : m_plugins) {
|
||||
if (!plugin->hasParent()) {
|
||||
|
@ -485,6 +489,7 @@ void MainWindow::initWalletContext() {
|
|||
});
|
||||
|
||||
connect(m_wallet, &Wallet::multiBroadcast, this, &MainWindow::onMultiBroadcast);
|
||||
connect(m_wallet->mmsStore(), &MultisigMessageStore::askToSign, this, &MainWindow::onAskToSign);
|
||||
}
|
||||
|
||||
void MainWindow::menuToggleTabVisible(const QString &key){
|
||||
|
@ -523,6 +528,8 @@ QString MainWindow::walletKeysPath() {
|
|||
}
|
||||
|
||||
void MainWindow::onWalletOpened() {
|
||||
qDebug() << this->thread();
|
||||
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_splashDialog->hide();
|
||||
|
||||
|
@ -553,6 +560,13 @@ void MainWindow::onWalletOpened() {
|
|||
m_coinsWidget->setModel(m_wallet->coinsModel(), m_wallet->coins());
|
||||
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
|
||||
connect(m_wallet->coins(), &Coins::descriptionChanged, [this] {
|
||||
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) {
|
||||
if (height >= (target - 1)) {
|
||||
this->updateNetStats();
|
||||
|
@ -957,8 +1012,17 @@ void MainWindow::onTransactionCreated(PendingTransaction *tx, const QVector<QStr
|
|||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (dialog.showAdvanced) {
|
||||
|
@ -1037,7 +1101,7 @@ void MainWindow::showSeedDialog() {
|
|||
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",
|
||||
{"To obtain wallet keys go to Wallet -> Keys"}, "show_wallet_seed");
|
||||
return;
|
||||
|
@ -1047,6 +1111,12 @@ void MainWindow::showSeedDialog() {
|
|||
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};
|
||||
dialog.exec();
|
||||
}
|
||||
|
@ -1682,6 +1752,10 @@ void MainWindow::updateTitle() {
|
|||
title += " [view-only]";
|
||||
}
|
||||
|
||||
if (m_wallet->isMultisig()) {
|
||||
title += " [multisig]";
|
||||
}
|
||||
|
||||
title += " - Feather";
|
||||
|
||||
this->setWindowTitle(title);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "SendWidget.h"
|
||||
#include "ReceiveWidget.h"
|
||||
#include "CoinsWidget.h"
|
||||
#include "MMSWidget.h"
|
||||
|
||||
#include "WindowManager.h"
|
||||
#include "plugins/Plugin.h"
|
||||
|
@ -165,6 +166,7 @@ private slots:
|
|||
void onProxySettingsChanged();
|
||||
void onOfflineMode(bool offline);
|
||||
void onMultiBroadcast(const QMap<QString, QString> &txHexMap);
|
||||
void onAskToSign(PendingTransaction *tx);
|
||||
|
||||
private:
|
||||
friend WindowManager;
|
||||
|
@ -220,6 +222,7 @@ private:
|
|||
SendWidget *m_sendWidget = nullptr;
|
||||
ReceiveWidget *m_receiveWidget = nullptr;
|
||||
CoinsWidget *m_coinsWidget = nullptr;
|
||||
MMSWidget *m_mmsWidget = nullptr;
|
||||
|
||||
QPointer<QAction> m_clearRecentlyOpenAction;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
|
@ -158,6 +158,20 @@
|
|||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -243,6 +243,9 @@ void SendWidget::sendClicked() {
|
|||
#endif
|
||||
}
|
||||
|
||||
// TODO: force key image sync here for multisig wallets?
|
||||
// if (m_wallet->isMultisig() && m_wallet->mul)
|
||||
|
||||
m_wallet->createTransaction(recipient, amount, description, sendAll);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ WindowManager::WindowManager(QObject *parent)
|
|||
m_splashDialog = new SplashDialog();
|
||||
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::deviceButtonRequest, this, &WindowManager::onDeviceButtonRequest);
|
||||
connect(m_walletManager, &WalletManager::deviceButtonPressed, this, &WindowManager::onDeviceButtonPressed);
|
||||
|
@ -75,6 +75,16 @@ void WindowManager::quitAfterLastWindow() {
|
|||
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.";
|
||||
this->close();
|
||||
}
|
||||
|
@ -85,10 +95,18 @@ void WindowManager::close() {
|
|||
window->close();
|
||||
}
|
||||
|
||||
m_wizard->deleteLater();
|
||||
m_splashDialog->deleteLater();
|
||||
m_tray->deleteLater();
|
||||
m_docsDialog->deleteLater();
|
||||
if (m_splashDialog) {
|
||||
m_splashDialog->deleteLater();
|
||||
}
|
||||
if (m_tray) {
|
||||
m_tray->deleteLater();
|
||||
}
|
||||
if (m_wizard) {
|
||||
m_wizard->deleteLater();
|
||||
}
|
||||
if (m_docsDialog) {
|
||||
m_docsDialog->deleteLater();
|
||||
}
|
||||
|
||||
torManager()->stop();
|
||||
|
||||
|
@ -296,10 +314,28 @@ void WindowManager::onWalletOpened(Wallet *wallet) {
|
|||
|
||||
m_splashDialog->hide();
|
||||
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);
|
||||
m_windows.append(window);
|
||||
this->buildTrayMenu();
|
||||
|
||||
m_openingWallet = false;
|
||||
|
||||
this->buildTrayMenu();
|
||||
}
|
||||
|
||||
void WindowManager::onWalletOpenPasswordRequired(bool invalidPassword, const QString &path) {
|
||||
|
@ -338,7 +374,7 @@ bool WindowManager::autoOpenWallet() {
|
|||
// ######################## WALLET CREATION ########################
|
||||
|
||||
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)) {
|
||||
this->handleWalletError({nullptr, Utils::ERROR, "Failed to create wallet", QString("File already exists: %1").arg(path)});
|
||||
return;
|
||||
|
@ -366,7 +402,14 @@ void WindowManager::tryCreateWallet(Seed seed, const QString &path, const QStrin
|
|||
wallet->setCacheAttribute("feather.seedoffset", seedOffset);
|
||||
|
||||
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);
|
||||
|
@ -417,6 +460,26 @@ void WindowManager::tryCreateWalletFromKeys(const QString &path, const QString &
|
|||
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) {
|
||||
// Currently only called when a wallet is created from device.
|
||||
auto state = wallet->status();
|
||||
|
@ -686,6 +749,8 @@ WalletWizard* WindowManager::createWizard(WalletWizard::Page startPage) {
|
|||
connect(wizard, &WalletWizard::createWallet, this, &WindowManager::tryCreateWallet);
|
||||
connect(wizard, &WalletWizard::createWalletFromKeys, this, &WindowManager::tryCreateWalletFromKeys);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -698,12 +763,17 @@ void WindowManager::initWizard() {
|
|||
this->showWizard(startPage);
|
||||
}
|
||||
|
||||
void WindowManager::showWizard(WalletWizard::Page startPage) {
|
||||
void WindowManager::showWizard(WalletWizard::Page startPage, Wallet *wallet) {
|
||||
if (!m_wizard) {
|
||||
m_wizard = this->createWizard(startPage);
|
||||
}
|
||||
|
||||
m_wizard->resetFields();
|
||||
|
||||
if (wallet) {
|
||||
m_wizard->setWallet(wallet);
|
||||
}
|
||||
|
||||
m_wizard->setStartId(startPage);
|
||||
m_wizard->restart();
|
||||
m_wizard->setEnabled(true);
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
void wizardOpenWallet();
|
||||
void close();
|
||||
void closeWindow(MainWindow *window);
|
||||
void showWizard(WalletWizard::Page startPage);
|
||||
void showWizard(WalletWizard::Page startPage, Wallet *wallet = nullptr);
|
||||
void restartApplication(const QString &binaryFilename);
|
||||
void raise();
|
||||
|
||||
|
@ -68,9 +68,10 @@ private slots:
|
|||
void onChangeTheme(const QString &themeName);
|
||||
|
||||
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 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();
|
||||
|
||||
|
@ -107,6 +108,7 @@ private:
|
|||
bool m_openWalletTriedOnce = false;
|
||||
bool m_openingWallet = false;
|
||||
bool m_initialNetworkConfigured = false;
|
||||
bool m_aboutToShowWizard = false;
|
||||
|
||||
QThread *m_cleanupThread;
|
||||
};
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
<file>assets/images/eye_blind.png</file>
|
||||
<file>assets/images/file.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/hd_32px.png</file>
|
||||
<file>assets/images/history.png</file>
|
||||
|
@ -58,6 +59,7 @@
|
|||
<file>assets/images/localMonero_logo.png</file>
|
||||
<file>assets/images/localMonero_register.svg</file>
|
||||
<file>assets/images/lock.svg</file>
|
||||
<file>assets/images/mail.png</file>
|
||||
<file>assets/images/microphone.png</file>
|
||||
<file>assets/images/mining.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);
|
||||
|
||||
this->update();
|
||||
this->updateSelection();
|
||||
// this->updateSelection();
|
||||
}
|
||||
|
||||
void AccountSwitcherDialog::update() {
|
||||
|
|
|
@ -21,28 +21,35 @@ SeedDialog::SeedDialog(Wallet *wallet, QWidget *parent)
|
|||
m_wallet->setSeedLanguage(constants::seedLanguage);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (m_wallet->isMultisig()) {
|
||||
this->setMultisigSeed(m_wallet->getMultisigSeed());
|
||||
ui->frameSeedOffset->setVisible(false);
|
||||
ui->check_toggleSeedType->hide();
|
||||
this->setSeed(seed_25_words);
|
||||
} else {
|
||||
this->setSeed(seed);
|
||||
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, "
|
||||
"specifying this block number will recover your wallet quicker.", "restore_height");
|
||||
|
||||
|
@ -59,10 +66,23 @@ void SeedDialog::setSeed(const QString &seed) {
|
|||
"</p>"
|
||||
"<b>WARNING:</b>"
|
||||
"<ul>"
|
||||
"<li>Never disclose your seed.</li>"
|
||||
"<li>Never disclose your seed</li>"
|
||||
"<li>Never type it on a website</li>"
|
||||
"<li>Do not store it electronically</li>"
|
||||
"</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;
|
|
@ -23,6 +23,7 @@ public:
|
|||
|
||||
private:
|
||||
void setSeed(const QString &seed);
|
||||
void setMultisigSeed(const QString &seed);
|
||||
|
||||
QScopedPointer<Ui::SeedDialog> ui;
|
||||
Wallet *m_wallet;
|
||||
|
|
|
@ -103,6 +103,38 @@ void TxConfAdvDialog::setUnsignedTransaction(UnsignedTransaction *utx) {
|
|||
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) {
|
||||
QString preferredCur = conf()->get(Config::preferredFiatCurrency).toString();
|
||||
|
||||
|
@ -180,6 +212,29 @@ void TxConfAdvDialog::setupConstructionData(ConstructionInfo *ci) {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
void setTransaction(PendingTransaction *tx, bool isSigned = true); // #TODO: have libwallet return a UnsignedTransaction, this is just dumb
|
||||
void setUnsignedTransaction(UnsignedTransaction *utx);
|
||||
void setMultisigTransaction(PendingTransaction *tx);
|
||||
|
||||
private:
|
||||
void setupConstructionData(ConstructionInfo *ci);
|
||||
|
@ -51,6 +52,7 @@ private:
|
|||
QMenu *m_exportTxKeyMenu;
|
||||
QString m_txid;
|
||||
bool m_offline;
|
||||
bool m_multisig = false;
|
||||
};
|
||||
|
||||
#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->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);
|
||||
|
||||
this->adjustSize();
|
||||
}
|
||||
|
||||
quint32 TxConfDialog::getMultisigSignerIndex() {
|
||||
return ui->combo_sendTo->currentIndex();
|
||||
}
|
||||
|
||||
void TxConfDialog::setShowAdvanced() {
|
||||
this->showAdvanced = true;
|
||||
QDialog::reject();
|
||||
|
|
|
@ -23,6 +23,8 @@ public:
|
|||
explicit TxConfDialog(Wallet *wallet, PendingTransaction *tx, const QString &address, const QString &description, QWidget *parent = nullptr);
|
||||
~TxConfDialog() override;
|
||||
|
||||
quint32 getMultisigSignerIndex();
|
||||
|
||||
bool showAdvanced = false;
|
||||
|
||||
private:
|
||||
|
|
|
@ -119,6 +119,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</item>
|
||||
</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_description = m_wallet->getCacheAttribute(QString("coin.description:%1").arg(ci->m_pubKey));
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ Q_OBJECT
|
|||
public:
|
||||
bool coin(int index, std::function<void (CoinsInfo &)> callback);
|
||||
CoinsInfo * coin(int index);
|
||||
void refresh();
|
||||
void refreshUnlocked();
|
||||
void freeze(QString &publicKey);
|
||||
void thaw(QString &publicKey);
|
||||
|
@ -41,6 +40,9 @@ public:
|
|||
quint64 count() const;
|
||||
void clearRows();
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
|
||||
signals:
|
||||
void refreshStarted() 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;
|
||||
}
|
||||
|
||||
QStringList PendingTransaction::prefixHashes() const
|
||||
{
|
||||
QStringList prefixHashes;
|
||||
for (const auto &hash : m_pimpl->prefixHashes()) {
|
||||
prefixHashes.append(QString::fromStdString(hash));
|
||||
}
|
||||
return prefixHashes;
|
||||
}
|
||||
|
||||
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)
|
||||
: QObject(parent)
|
||||
, m_pimpl(pt)
|
||||
|
|
|
@ -31,12 +31,23 @@ public:
|
|||
quint64 dust() const;
|
||||
quint64 fee() const;
|
||||
QStringList txid() const;
|
||||
QStringList prefixHashes() const;
|
||||
quint64 txCount() const;
|
||||
QList<QVariant> subaddrIndices() const;
|
||||
std::string unsignedTxToBin() const;
|
||||
QString unsignedTxToBase64() const;
|
||||
QString signedTxToHex(int index) const;
|
||||
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;
|
||||
|
||||
|
|
|
@ -185,6 +185,10 @@ void TransactionHistory::refresh()
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "AddressBook.h"
|
||||
#include "Coins.h"
|
||||
#include "MultisigMessageStore.h"
|
||||
#include "Subaddress.h"
|
||||
#include "SubaddressAccount.h"
|
||||
#include "TransactionHistory.h"
|
||||
|
@ -22,6 +23,8 @@
|
|||
#include "model/SubaddressModel.h"
|
||||
#include "model/SubaddressAccountModel.h"
|
||||
#include "model/CoinsModel.h"
|
||||
#include "model/MultisigMessageModel.h"
|
||||
#include "model/MultisigIncomingTxModel.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_refreshNow(false)
|
||||
, m_refreshEnabled(false)
|
||||
, m_mmsRefreshEnabled(false)
|
||||
, m_scheduler(this)
|
||||
, m_useSSL(true)
|
||||
, m_coins(new Coins(this, wallet->getWallet(), this))
|
||||
, m_mmsStore(new MultisigMessageStore(this, wallet->getWallet(), this))
|
||||
, m_storeTimer(new QTimer(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_subaddressAccountModel = new SubaddressAccountModel(this, m_subaddressAccount);
|
||||
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) {
|
||||
startRefreshThread();
|
||||
|
@ -86,6 +93,9 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
|
|||
connect(m_subaddress, &Subaddress::corrupted, [this]{
|
||||
emit keysCorrupted();
|
||||
});
|
||||
|
||||
connect(m_mmsStore, &MultisigMessageStore::multisigInfoImported, m_coins, &Coins::refresh);
|
||||
connect(m_mmsStore, &MultisigMessageStore::multisigInfoExported, m_coins, &Coins::refresh);
|
||||
}
|
||||
|
||||
// #################### Status ####################
|
||||
|
@ -128,6 +138,12 @@ bool Wallet::viewOnly() const {
|
|||
return m_wallet2->watch_only();
|
||||
}
|
||||
|
||||
bool Wallet::isMultisig() const {
|
||||
bool ready, multisig;
|
||||
multisig = m_wallet2->multisig(&ready);
|
||||
return multisig;
|
||||
}
|
||||
|
||||
bool Wallet::isDeterministic() const {
|
||||
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()));
|
||||
}
|
||||
|
||||
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 {
|
||||
auto seedLength = this->getCacheAttribute("feather.seed").split(" ", Qt::SkipEmptyParts).length();
|
||||
return seedLength ? seedLength : 25;
|
||||
|
@ -420,47 +442,54 @@ void Wallet::startRefreshThread()
|
|||
const auto future = m_scheduler.run([this] {
|
||||
// Beware! This code does not run in the GUI thread.
|
||||
|
||||
constexpr const std::chrono::seconds refreshInterval{10};
|
||||
constexpr const std::chrono::milliseconds intervalResolution{100};
|
||||
|
||||
auto last = std::chrono::steady_clock::now();
|
||||
while (!m_scheduler.stopping())
|
||||
{
|
||||
if (m_refreshEnabled && (!isHwBacked() || isDeviceConnected()))
|
||||
if ((!isHwBacked() || isDeviceConnected()))
|
||||
{
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
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
|
||||
// daemonHeight and targetHeight will be 0 if call to get_info fails
|
||||
quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight();
|
||||
bool success = daemonHeight > 0;
|
||||
// get daemonHeight and targetHeight
|
||||
// daemonHeight and targetHeight will be 0 if call to get_info fails
|
||||
quint64 daemonHeight = m_walletImpl->daemonBlockChainHeight();
|
||||
bool success = daemonHeight > 0;
|
||||
|
||||
quint64 targetHeight = 0;
|
||||
if (success) {
|
||||
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;
|
||||
quint64 targetHeight = 0;
|
||||
if (success) {
|
||||
targetHeight = m_walletImpl->daemonBlockChainTargetHeight();
|
||||
}
|
||||
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() {
|
||||
this->updateBalance();
|
||||
if (this->isSynchronized()) {
|
||||
// m_mmsStore->exportMultisig();
|
||||
this->refreshModels();
|
||||
}
|
||||
}
|
||||
|
@ -563,6 +593,11 @@ void Wallet::refreshModels() {
|
|||
m_history->refresh();
|
||||
m_coins->refresh();
|
||||
this->subaddress()->refresh(this->currentSubaddressAccount());
|
||||
m_mmsStore->refresh();
|
||||
}
|
||||
|
||||
void Wallet::setRefreshInterval(qint64 seconds) {
|
||||
m_refreshInterval = std::chrono::seconds{seconds};
|
||||
}
|
||||
|
||||
// #################### Hardware wallet ####################
|
||||
|
@ -1023,6 +1058,13 @@ PendingTransaction * Wallet::loadSignedTxFromStr(const std::string &data)
|
|||
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
|
||||
{
|
||||
qDebug() << "Trying to submit " << fileName;
|
||||
|
@ -1103,6 +1145,18 @@ CoinsModel* Wallet::coinsModel() const {
|
|||
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 ####################
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// #################### 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 ####################
|
||||
|
||||
quint64 Wallet::getBytesReceived() const {
|
||||
|
|
|
@ -71,6 +71,9 @@ class SubaddressAccount;
|
|||
class SubaddressAccountModel;
|
||||
class Coins;
|
||||
class CoinsModel;
|
||||
class MultisigMessageStore;
|
||||
class MultisigMessageModel;
|
||||
class MultisigIncomingTxModel;
|
||||
|
||||
struct TxProofResult {
|
||||
TxProofResult() {}
|
||||
|
@ -133,6 +136,8 @@ public:
|
|||
//! returns if view only wallet
|
||||
bool viewOnly() const;
|
||||
|
||||
bool isMultisig() const;
|
||||
|
||||
//! return true if deterministic keys
|
||||
bool isDeterministic() const;
|
||||
|
||||
|
@ -175,6 +180,7 @@ public:
|
|||
|
||||
//! returns mnemonic seed
|
||||
QString getSeed(const QString &seedOffset) const;
|
||||
QString getMultisigSeed() const;
|
||||
|
||||
qsizetype seedLength() const;
|
||||
|
||||
|
@ -227,6 +233,8 @@ public:
|
|||
|
||||
void refreshModels();
|
||||
|
||||
void setRefreshInterval(qint64 seconds);
|
||||
|
||||
// ##### Hardware wallet #####
|
||||
bool isHwBacked() const;
|
||||
bool isLedger() const;
|
||||
|
@ -342,6 +350,8 @@ public:
|
|||
PendingTransaction * loadSignedTxFile(const QString &fileName);
|
||||
PendingTransaction * loadSignedTxFromStr(const std::string &data);
|
||||
|
||||
PendingTransaction * restoreMultisigTransaction(const std::string &data);
|
||||
|
||||
//! Submit a transfer from file
|
||||
bool submitTxFile(const QString &fileName) const;
|
||||
|
||||
|
@ -359,6 +369,9 @@ public:
|
|||
SubaddressAccountModel* subaddressAccountModel() const;
|
||||
Coins* coins() const;
|
||||
CoinsModel* coinsModel() const;
|
||||
MultisigMessageModel* mmsModel() const;
|
||||
MultisigIncomingTxModel* msIncomingTxModel() const;
|
||||
MultisigMessageStore* mmsStore() const;
|
||||
|
||||
// ##### Transaction proofs #####
|
||||
|
||||
|
@ -386,6 +399,20 @@ public:
|
|||
|
||||
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 #####
|
||||
|
||||
quint64 getBytesReceived() const;
|
||||
|
@ -498,6 +525,10 @@ private:
|
|||
Coins *m_coins;
|
||||
CoinsModel *m_coinsModel;
|
||||
|
||||
MultisigMessageStore *m_mmsStore;
|
||||
MultisigMessageModel *m_mmsModel;
|
||||
MultisigIncomingTxModel *m_msIncomingTxModel;
|
||||
|
||||
QMutex m_asyncMutex;
|
||||
QString m_daemonUsername;
|
||||
QString m_daemonPassword;
|
||||
|
@ -505,6 +536,8 @@ private:
|
|||
QMutex m_proxyMutex;
|
||||
std::atomic<bool> m_refreshNow;
|
||||
std::atomic<bool> m_refreshEnabled;
|
||||
std::atomic<bool> m_mmsRefreshEnabled;
|
||||
std::chrono::seconds m_refreshInterval{10};
|
||||
WalletListenerImpl *m_walletListener;
|
||||
FutureScheduler m_scheduler;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "WalletListenerImpl.h"
|
||||
#include "Wallet.h"
|
||||
#include "WalletManager.h"
|
||||
#include "MultisigMessageStore.h"
|
||||
|
||||
WalletListenerImpl::WalletListenerImpl(Wallet * w)
|
||||
: m_wallet(w)
|
||||
|
@ -31,6 +32,11 @@ void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
|
|||
QString qTxId = QString::fromStdString(txId);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,9 +95,12 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
|
|||
auto wallet = new Wallet(w);
|
||||
|
||||
// move wallet to the GUI thread. Otherwise it wont be emitting signals
|
||||
qDebug() << wallet->thread();
|
||||
qDebug() << qApp->thread();
|
||||
if (wallet->thread() != qApp->thread()) {
|
||||
wallet->moveToThread(qApp->thread());
|
||||
}
|
||||
qDebug() << wallet->thread();
|
||||
|
||||
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
|
||||
{
|
||||
return m_pimpl->walletExists(path.toStdString());
|
||||
|
|
|
@ -58,6 +58,15 @@ public:
|
|||
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,
|
||||
const QString &password,
|
||||
const QString &language,
|
||||
|
|
|
@ -124,6 +124,18 @@ QString CoinsInfo::txNote() const {
|
|||
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)
|
||||
: QObject(parent)
|
||||
, m_blockHeight(0)
|
||||
|
@ -142,6 +154,8 @@ CoinsInfo::CoinsInfo(QObject *parent)
|
|||
, m_unlocked(false)
|
||||
, m_coinbase(false)
|
||||
, m_change(false)
|
||||
, m_keyImagePartial(false)
|
||||
, m_haveMultisigK(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ public:
|
|||
QString description() const;
|
||||
bool change() const;
|
||||
QString txNote() const;
|
||||
bool keyImagePartial() const;
|
||||
bool haveMultisigK() const;
|
||||
QStringList multisigInfo() const;
|
||||
|
||||
void setUnlocked(bool unlocked);
|
||||
|
||||
|
@ -71,6 +74,9 @@ private:
|
|||
QString m_description;
|
||||
bool m_change;
|
||||
QString m_txNote;
|
||||
bool m_keyImagePartial;
|
||||
bool m_haveMultisigK;
|
||||
QStringList m_multisigInfo;
|
||||
};
|
||||
|
||||
#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";
|
||||
}
|
||||
return rings;
|
||||
}
|
||||
|
||||
QString TransactionRow::prefixHash() const {
|
||||
return m_prefixHash;
|
||||
}
|
|
@ -50,6 +50,7 @@ public:
|
|||
QList<QString> destinations() const;
|
||||
QList<Transfer*> transfers() const;
|
||||
QString rings_formatted() const;
|
||||
QString prefixHash() const;
|
||||
|
||||
private:
|
||||
explicit TransactionRow();
|
||||
|
@ -76,6 +77,7 @@ private:
|
|||
QDateTime m_timestamp;
|
||||
quint64 m_unlockTime;
|
||||
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 <QtCore>
|
||||
#include <QtGui>
|
||||
#include <singleapplication.h>
|
||||
//#include <singleapplication.h>
|
||||
|
||||
#include "config-feather.h"
|
||||
#include "constants.h"
|
||||
|
@ -88,6 +88,11 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
qputenv("QT_QPA_PLATFORM", "windows:darkmode=1");
|
||||
#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_;
|
||||
for(int i = 0; i != argc; i++){
|
||||
argv_ << QString::fromStdString(argv[i]);
|
||||
|
@ -137,7 +142,7 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
|
||||
#endif
|
||||
|
||||
SingleApplication app(argc, argv);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
QApplication::setApplicationName("FeatherWallet");
|
||||
|
@ -243,10 +248,11 @@ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
|
|||
|
||||
auto wm = windowManager();
|
||||
wm->setEventFilter(&filter);
|
||||
wm->raise();
|
||||
|
||||
QObject::connect(&app, &SingleApplication::instanceStarted, [&wm]() {
|
||||
wm->raise();
|
||||
});
|
||||
// QObject::connect(&app, &SingleApplication::instanceStarted, [&wm]() {
|
||||
// wm->raise();
|
||||
// });
|
||||
|
||||
return QApplication::exec();
|
||||
}
|
||||
|
|
|
@ -90,12 +90,22 @@ QVariant CoinsModel::data(const QModelIndex &index, int role) const
|
|||
switch (index.column()) {
|
||||
case KeyImageKnown:
|
||||
{
|
||||
if (cInfo.keyImageKnown()) {
|
||||
result = QVariant(icons()->icon("eye1.png"));
|
||||
}
|
||||
else {
|
||||
if (!cInfo.keyImageKnown() || cInfo.keyImagePartial()) {
|
||||
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 {
|
||||
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()) {
|
||||
|
@ -148,6 +173,8 @@ QVariant CoinsModel::headerData(int section, Qt::Orientation orientation, int ro
|
|||
switch(section) {
|
||||
case PubKey:
|
||||
return QString("Pub Key");
|
||||
case MultisigInfo:
|
||||
return QString("Multisig info");
|
||||
case TxID:
|
||||
return QString("TxID");
|
||||
case BlockHeight:
|
||||
|
@ -217,6 +244,10 @@ QVariant CoinsModel::parseTransactionInfo(const CoinsInfo &cInfo, int column, in
|
|||
{
|
||||
case KeyImageKnown:
|
||||
return "";
|
||||
case HaveMultisigK:
|
||||
return "";
|
||||
case MultisigInfo:
|
||||
return cInfo.multisigInfo().join(", ");
|
||||
case PubKey:
|
||||
return cInfo.pubKey().mid(0,8);
|
||||
case TxID:
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
enum ModelColumn
|
||||
{
|
||||
KeyImageKnown = 0,
|
||||
HaveMultisigK,
|
||||
MultisigInfo,
|
||||
PubKey,
|
||||
TxID,
|
||||
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;
|
||||
return amount;
|
||||
}
|
||||
// case Column::TxPrefixHash:
|
||||
// {
|
||||
// return tInfo.prefixHash().mid(0, 8);
|
||||
// }
|
||||
case Column::TxID: {
|
||||
if (conf()->get(Config::historyShowFullTxid).toBool()) {
|
||||
return tInfo.hash();
|
||||
|
@ -217,6 +221,8 @@ QVariant TransactionHistoryModel::headerData(int section, Qt::Orientation orient
|
|||
return QString("Amount");
|
||||
case Column::TxID:
|
||||
return QString("Txid");
|
||||
// case Column::TxPrefixHash:
|
||||
// return QString("Prefix hash");
|
||||
case Column::FiatAmount:
|
||||
return QString("Fiat");
|
||||
default:
|
||||
|
|
|
@ -513,6 +513,14 @@ QString displayAddress(const QString& address, int sections, const QString& 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 rec;
|
||||
if (index.isPrimary()) {
|
||||
|
@ -684,4 +692,13 @@ QString formatSyncStatus(quint64 height, quint64 target, bool daemonSync) {
|
|||
|
||||
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);
|
||||
|
||||
QString displayAddress(const QString& address, int sections = 3, const QString & sep = " ");
|
||||
QString chunkAddress(const QString& address);
|
||||
QTextCharFormat addressTextFormat(const SubaddressIndex &index, quint64 amount);
|
||||
|
||||
QFont getMonospaceFont();
|
||||
|
@ -116,6 +117,7 @@ namespace Utils
|
|||
void clearLayout(QLayout *layout, bool deleteWidgets = true);
|
||||
|
||||
QString formatSyncStatus(quint64 height, quint64 target, bool daemonSync = false);
|
||||
QString formatRestoreHeight(Wallet *wallet);
|
||||
}
|
||||
|
||||
#endif //FEATHER_UTILS_H
|
||||
|
|
|
@ -21,6 +21,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
|||
{Config::warnOnStagenet,{QS("warnOnStagenet"), true}},
|
||||
{Config::warnOnTestnet,{QS("warnOnTestnet"), true}},
|
||||
{Config::warnOnKiImport,{QS("warnOnKiImport"), true}},
|
||||
{Config::warnOnMultisigExperimental,{QS("warnOnMultisigExperimental"), true}},
|
||||
{Config::logLevel,{QS("logLevel"), 0}},
|
||||
|
||||
{Config::homeWidget,{QS("homeWidget"), "ccs"}},
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
warnOnStagenet,
|
||||
warnOnTestnet,
|
||||
warnOnKiImport,
|
||||
warnOnMultisigExperimental,
|
||||
|
||||
homeWidget,
|
||||
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->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;
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>386</width>
|
||||
<height>152</height>
|
||||
<width>576</width>
|
||||
<height>230</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -109,6 +109,67 @@
|
|||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</widget>
|
||||
<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->combo_deviceType->addItem("Ledger Nano S (PLUS) / X", DeviceType::LEDGER);
|
||||
ui->combo_deviceType->addItem("Trezor Model T / Safe 3", DeviceType::TREZOR);
|
||||
this->setTitle("Restore from hardware device");
|
||||
|
||||
connect(ui->btnOptions, &QPushButton::clicked, this, &PageHardwareDevice::onOptionsClicked);
|
||||
}
|
||||
|
||||
void PageHardwareDevice::initializePage() {
|
||||
ui->radioNewWallet->setChecked(true);
|
||||
ui->radio_create->setChecked(true);
|
||||
}
|
||||
|
||||
int PageHardwareDevice::nextId() const {
|
||||
|
@ -35,8 +34,13 @@ int PageHardwareDevice::nextId() const {
|
|||
}
|
||||
|
||||
bool PageHardwareDevice::validatePage() {
|
||||
m_fields->deviceType = static_cast<DeviceType>(ui->combo_deviceType->currentData().toInt());
|
||||
m_fields->showSetRestoreHeightPage = ui->radioRestoreWallet->isChecked();
|
||||
if (ui->radio_ledger->isChecked()) {
|
||||
m_fields->deviceType = DeviceType::LEDGER;
|
||||
} else {
|
||||
m_fields->deviceType = DeviceType::TREZOR;
|
||||
}
|
||||
|
||||
m_fields->showSetRestoreHeightPage = ui->radio_restore->isChecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,31 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Select device type:</string>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Select device type: </string>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_deviceType"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
|
@ -32,79 +48,31 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="radioNewWallet">
|
||||
<property name="text">
|
||||
<string>Create a new wallet file from device</string>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Does the wallet currently hold any funds? </string>
|
||||
</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>
|
||||
</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>
|
||||
<spacer name="verticalSpacer">
|
||||
<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));
|
||||
|
||||
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() {
|
||||
if (m_walletKeysFilesModel->rowCount() > 0) {
|
||||
ui->radioOpen->setChecked(true);
|
||||
ui->radio_open->setChecked(true);
|
||||
} else {
|
||||
ui->radioCreate->setChecked(true);
|
||||
ui->radio_create->setChecked(true);
|
||||
}
|
||||
|
||||
// Don't show setup wizard again
|
||||
|
@ -35,15 +56,19 @@ void PageMenu::initializePage() {
|
|||
}
|
||||
|
||||
int PageMenu::nextId() const {
|
||||
if (ui->radioCreate->isChecked())
|
||||
if (ui->radio_create->isChecked())
|
||||
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;
|
||||
if (ui->radioSeed->isChecked())
|
||||
if (ui->radio_restoreSeed->isChecked())
|
||||
return WalletWizard::Page_WalletRestoreSeed;
|
||||
if (ui->radioViewOnly->isChecked())
|
||||
if (ui->radio_restoreKeys->isChecked())
|
||||
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 0;
|
||||
}
|
||||
|
@ -51,23 +76,31 @@ int PageMenu::nextId() const {
|
|||
bool PageMenu::validatePage() {
|
||||
m_fields->clearFields();
|
||||
|
||||
if (ui->radioCreate->isChecked()) {
|
||||
if (ui->radio_create->isChecked()) {
|
||||
m_fields->mode = WizardMode::CreateWallet;
|
||||
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->modeText = "Open wallet";
|
||||
}
|
||||
if (ui->radioSeed->isChecked()) {
|
||||
if (ui->radio_restoreSeed->isChecked()) {
|
||||
m_fields->mode = WizardMode::RestoreFromSeed;
|
||||
m_fields->modeText = "Restore wallet";
|
||||
}
|
||||
if (ui->radioViewOnly->isChecked()) {
|
||||
if (ui->radio_restoreKeys->isChecked()) {
|
||||
m_fields->mode = WizardMode::RestoreFromKeys;
|
||||
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->modeText = "Create from hardware device";
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ private:
|
|||
Ui::PageMenu *ui;
|
||||
WalletKeysFilesModel *m_walletKeysFilesModel;
|
||||
WizardFields *m_fields;
|
||||
|
||||
WalletWizard::Page m_nextPage;
|
||||
};
|
||||
|
||||
#endif //FEATHER_WIZARDMENU_H
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>617</width>
|
||||
<height>463</height>
|
||||
<width>610</width>
|
||||
<height>465</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -15,63 +15,92 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<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>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</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>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -82,24 +111,17 @@
|
|||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<height>73</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_version">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>by dsc & tobtoht</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLabel" name="label_version">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</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)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
this->setFinalPage(true);
|
||||
|
||||
ui->frame_password->setInfo(icons()->icon("lock"), "Choose a password to encrypt your wallet keys.");
|
||||
|
||||
connect(ui->widget_password, &PasswordSetWidget::passwordEntryChanged, [this]{
|
||||
this->completeChanged();
|
||||
});
|
||||
|
||||
this->setButtonText(QWizard::FinishButton, "Create/Open wallet");
|
||||
}
|
||||
|
||||
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);
|
||||
ui->widget_password->resetFields();
|
||||
}
|
||||
|
||||
bool PageSetPassword::validatePage() {
|
||||
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;
|
||||
}
|
||||
|
||||
int PageSetPassword::nextId() const {
|
||||
if (m_fields->mode == WizardMode::CreateMultisig) {
|
||||
return WalletWizard::Page_MultisigCreateSetupKey;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,12 @@ public:
|
|||
|
||||
signals:
|
||||
void createWallet();
|
||||
void createMultisigWallet();
|
||||
|
||||
private:
|
||||
Ui::PageSetPassword *ui;
|
||||
|
||||
bool m_walletCreated = false;
|
||||
WizardFields *m_fields;
|
||||
};
|
||||
|
||||
|
|
|
@ -120,6 +120,9 @@ QString PageWalletFile::defaultWalletName() {
|
|||
walletStr = QString("trezor_%1");
|
||||
}
|
||||
}
|
||||
else if (m_fields->mode == WizardMode::CreateMultisig || m_fields->mode == WizardMode::RestoreMultisig) {
|
||||
walletStr = QString("multisig_%1");
|
||||
}
|
||||
walletName = walletStr.arg(count);
|
||||
count++;
|
||||
} while (this->walletPathExists(walletName));
|
||||
|
|
|
@ -77,6 +77,7 @@ PageWalletRestoreKeys::PageWalletRestoreKeys(WizardFields *fields, QWidget *pare
|
|||
}
|
||||
|
||||
void PageWalletRestoreKeys::initializePage() {
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
this->showInputLines();
|
||||
}
|
||||
|
||||
|
@ -97,12 +98,18 @@ void PageWalletRestoreKeys::showInputLines() {
|
|||
ui->frame_viewKey->hide();
|
||||
ui->frame_spendKey->show();
|
||||
}
|
||||
else {
|
||||
else if (ui->combo_walletType->currentIndex() == walletType::Spendable_Nondeterministic){
|
||||
ui->frame_address->show();
|
||||
ui->frame_viewKey->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_viewkey->setText("");
|
||||
ui->line_spendkey->setText("");
|
||||
|
@ -111,6 +118,12 @@ void PageWalletRestoreKeys::showInputLines() {
|
|||
bool PageWalletRestoreKeys::validatePage() {
|
||||
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_viewkey->setStyleSheet("");
|
||||
ui->label_errorString->hide();
|
||||
|
|
|
@ -23,7 +23,8 @@ class PageWalletRestoreKeys : public QWizardPage
|
|||
enum walletType {
|
||||
ViewOnly = 0,
|
||||
Spendable = 1,
|
||||
Spendable_Nondeterministic = 2
|
||||
Spendable_Nondeterministic = 2,
|
||||
Multisig = 3,
|
||||
};
|
||||
|
||||
public:
|
||||
|
|
|
@ -58,105 +58,167 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_spendKey">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<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_3">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">spend</span> key</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_spendkey"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_viewKey">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<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_viewKey">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">view</span> key</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_viewkey"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_address">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Primary address</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_address"/>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<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="QFrame" name="frame_spendKey">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<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_3">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">spend</span> key</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_spendkey"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_viewKey">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<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_viewKey">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Secret <span style=" font-weight:600;">view</span> key</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="line_viewkey"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_address">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="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>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -19,17 +19,32 @@
|
|||
#include "PageHardwareDevice.h"
|
||||
#include "PageNetworkProxy.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 "WindowManager.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QScreen>
|
||||
#include "PageRecoverWallet.h"
|
||||
#include "PageKeyType.h"
|
||||
|
||||
WalletWizard::WalletWizard(QWidget *parent)
|
||||
: QWizard(parent)
|
||||
{
|
||||
this->setWindowTitle("Welcome to Feather Wallet");
|
||||
this->setWindowTitle("Feather Wizard");
|
||||
this->setWindowIcon(QIcon(":/assets/images/appicons/64x64.png"));
|
||||
|
||||
m_walletKeysFilesModel = new WalletKeysFilesModel(this);
|
||||
|
@ -45,6 +60,9 @@ WalletWizard::WalletWizard(QWidget *parent)
|
|||
auto walletSetPasswordPage = new PageSetPassword(&m_wizardFields, this);
|
||||
auto walletSetSeedPassphrasePage = new PageSetSeedPassphrase(&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_WalletFile, createWallet);
|
||||
setPage(Page_OpenWallet, openWalletPage);
|
||||
|
@ -60,6 +78,25 @@ WalletWizard::WalletWizard(QWidget *parent)
|
|||
setPage(Page_SetSeedPassphrase, walletSetSeedPassphrasePage);
|
||||
setPage(Page_SetSubaddressLookahead, walletSetSubaddressLookaheadPage);
|
||||
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);
|
||||
|
||||
|
@ -80,6 +117,8 @@ WalletWizard::WalletWizard(QWidget *parent)
|
|||
layout << QWizard::CommitButton;
|
||||
this->setButtonLayout(layout);
|
||||
|
||||
this->setButtonText(QWizard::CommitButton, "Next");
|
||||
|
||||
auto *settingsButton = new QPushButton("Settings", this);
|
||||
this->setButton(QWizard::CustomButton1, settingsButton);
|
||||
|
||||
|
@ -102,6 +141,11 @@ WalletWizard::WalletWizard(QWidget *parent)
|
|||
emit openWallet(path, "");
|
||||
});
|
||||
|
||||
connect(multisigRecoveryInfo, &PageMultisigMMSRecoveryInfo::showWallet, [this](Wallet* wallet){
|
||||
m_wizardFields.wallet = nullptr;
|
||||
emit showWallet(wallet);
|
||||
});
|
||||
|
||||
connect(this, &QWizard::helpRequested, this, &WalletWizard::showHelp);
|
||||
}
|
||||
|
||||
|
@ -109,6 +153,10 @@ void WalletWizard::resetFields() {
|
|||
m_wizardFields = {};
|
||||
}
|
||||
|
||||
void WalletWizard::setWallet(Wallet *wallet) {
|
||||
m_wizardFields.wallet = wallet;
|
||||
}
|
||||
|
||||
void WalletWizard::onCreateWallet() {
|
||||
auto walletPath = QString("%1/%2").arg(m_wizardFields.walletDir, m_wizardFields.walletName);
|
||||
|
||||
|
@ -147,6 +195,21 @@ void WalletWizard::onCreateWallet() {
|
|||
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 (m_wizardFields.mode == WizardMode::CreateWallet && currentBlockHeight > 0) {
|
||||
qInfo() << "New wallet, setting restore height to latest blockheight: " << currentBlockHeight;
|
||||
|
@ -157,9 +220,10 @@ void WalletWizard::onCreateWallet() {
|
|||
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() {
|
||||
|
@ -195,4 +259,8 @@ void WalletWizard::showHelp() {
|
|||
if (!doc.isEmpty()) {
|
||||
windowManager()->showDocs(this, doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletWizard::~WalletWizard() {
|
||||
delete m_wizardFields.wallet;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ enum WizardMode {
|
|||
OpenWallet,
|
||||
RestoreFromSeed,
|
||||
RestoreFromKeys,
|
||||
CreateWalletFromDevice
|
||||
CreateWalletFromDevice,
|
||||
CreateMultisig,
|
||||
RestoreMultisig
|
||||
};
|
||||
|
||||
enum DeviceType {
|
||||
|
@ -27,6 +29,18 @@ enum DeviceType {
|
|||
TREZOR
|
||||
};
|
||||
|
||||
enum SignerConfig {
|
||||
AUTOMATIC = 0,
|
||||
SEMI_AUTOMATIC,
|
||||
MANUAL
|
||||
};
|
||||
|
||||
struct MMSSigner {
|
||||
quint32 index = 0;
|
||||
QString label;
|
||||
QString address;
|
||||
};
|
||||
|
||||
struct WizardFields {
|
||||
QString walletName;
|
||||
QString walletDir;
|
||||
|
@ -46,6 +60,17 @@ struct WizardFields {
|
|||
Seed::Type seedType;
|
||||
DeviceType deviceType;
|
||||
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() {
|
||||
showSetSeedPassphrasePage = false;
|
||||
|
@ -58,6 +83,7 @@ struct WizardFields {
|
|||
secretSpendKey = "";
|
||||
restoreHeight = 0;
|
||||
subaddressLookahead = "";
|
||||
wallet = nullptr;
|
||||
}
|
||||
|
||||
WizardFields(): deviceType(DeviceType::LEDGER), mode(WizardMode::CreateWallet),
|
||||
|
@ -78,26 +104,50 @@ public:
|
|||
Page_SetSubaddressLookahead,
|
||||
Page_OpenWallet,
|
||||
Page_Network,
|
||||
Page_Recover,
|
||||
Page_WalletRestoreSeed,
|
||||
Page_WalletRestoreKeys,
|
||||
Page_SetRestoreHeight,
|
||||
Page_HardwareDevice,
|
||||
Page_NetworkProxy,
|
||||
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);
|
||||
~WalletWizard() override;
|
||||
|
||||
void resetFields();
|
||||
void setWallet(Wallet* wallet);
|
||||
|
||||
signals:
|
||||
void initialNetworkConfigured();
|
||||
void showSettings();
|
||||
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 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:
|
||||
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