mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 19:49:28 +00:00
airgap: allow transmitting view-only details over UR
This commit is contained in:
parent
3dd6dbc9ba
commit
87aae77fda
13 changed files with 167 additions and 42 deletions
|
@ -405,6 +405,9 @@ void MainWindow::initOffline() {
|
|||
connect(ui->btn_help, &QPushButton::clicked, [this] {
|
||||
windowManager()->showDocs(this, "offline_tx_signing");
|
||||
});
|
||||
connect(ui->btn_viewOnlyDetails, &QPushButton::clicked, [this] {
|
||||
this->showViewOnlyDialog();
|
||||
});
|
||||
connect(ui->btn_checkAddress, &QPushButton::clicked, [this]{
|
||||
AddressCheckerIndexDialog dialog{m_wallet, this};
|
||||
dialog.exec();
|
||||
|
|
|
@ -403,6 +403,13 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_viewOnlyDetails">
|
||||
<property name="text">
|
||||
<string>View-only details</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_checkAddress">
|
||||
<property name="text">
|
||||
|
|
|
@ -8,12 +8,29 @@
|
|||
|
||||
#include "utils/Utils.h"
|
||||
|
||||
URDialog::URDialog(QWidget *parent)
|
||||
URDialog::URDialog(QWidget *parent, const QString &data, bool scanOnly)
|
||||
: WindowModalDialog(parent)
|
||||
, ui(new Ui::URDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
if (!data.isEmpty()) {
|
||||
ui->btn_loadFile->setVisible(false);
|
||||
ui->btn_loadClipboard->setVisible(false);
|
||||
ui->tabWidget->setTabVisible(1, false);
|
||||
|
||||
QScreen *currentScreen = QApplication::screenAt(this->geometry().center());
|
||||
if (!currentScreen) {
|
||||
currentScreen = QApplication::primaryScreen();
|
||||
}
|
||||
int availableHeight = currentScreen->availableGeometry().height() - 200;
|
||||
this->resize(availableHeight, availableHeight);
|
||||
|
||||
std::string d = data.toStdString();
|
||||
ui->widgetUR->setData("xmr-viewonly", d);
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ui->btn_loadFile, &QPushButton::clicked, [this]{
|
||||
QString fn = QFileDialog::getOpenFileName(this, "Load file", QDir::homePath(), "All Files (*)");
|
||||
if (fn.isEmpty()) {
|
||||
|
@ -41,7 +58,7 @@ URDialog::URDialog(QWidget *parent)
|
|||
|
||||
std::string data = qdata.toStdString();
|
||||
|
||||
ui->widgetUR->setData("ana", data);
|
||||
ui->widgetUR->setData("any", data);
|
||||
});
|
||||
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, [this](int index){
|
||||
|
@ -51,44 +68,76 @@ URDialog::URDialog(QWidget *parent)
|
|||
});
|
||||
|
||||
connect(ui->widgetScanner, &QrCodeScanWidget::finished, [this](bool success){
|
||||
if (!success) {
|
||||
if (!success) {
|
||||
Utils::showError(this, "Unable to scan UR");
|
||||
ui->widgetScanner->reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->radio_clipboard->isChecked()) {
|
||||
Utils::copyToClipboard(QString::fromStdString(ui->widgetScanner->getURData()));
|
||||
Utils::showInfo(this, "Data copied to clipboard");
|
||||
}
|
||||
else if (ui->radio_file->isChecked()) {
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save to file", QDir::homePath(), "ur_data");
|
||||
if (fn.isEmpty()) {
|
||||
ui->widgetScanner->reset();
|
||||
return;
|
||||
}
|
||||
if (ui->widgetScanner->getURType() == "xmr-viewonly") {
|
||||
QRegularExpression viewOnlyDetails(
|
||||
"Secret view key: (?<key>[0-9a-f]{64})\nAddress: (?<address>\\w+)\nRestore height: (?<restoreheight>\\d+)\nWallet name: (?<walletname>\\w+)\n",
|
||||
QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
|
||||
QString data = QString::fromStdString(ui->widgetScanner->getURData());
|
||||
QRegularExpressionMatch match = viewOnlyDetails.match(data);
|
||||
|
||||
QFile file{fn};
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
Utils::showError(this, "Failed to save file", QString("Could not open file %1 for writing").arg(fn));
|
||||
ui->widgetScanner->reset();
|
||||
return;
|
||||
}
|
||||
if (!match.hasMatch()) {
|
||||
Utils::showError(this, "Unable to load view-only details", "Unexpected data");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string data = ui->widgetScanner->getURData();
|
||||
file.write(data.data(), data.size());
|
||||
file.close();
|
||||
m_viewOnlyDetails.address = match.captured("address");
|
||||
m_viewOnlyDetails.key = match.captured("key").toLower();
|
||||
m_viewOnlyDetails.restoreHeight = match.captured("restoreheight").toInt();
|
||||
m_viewOnlyDetails.walletName = QString("%1_view_only").arg(match.captured("walletname"));
|
||||
|
||||
Utils::showInfo(this, "Successfully saved data to file");
|
||||
}
|
||||
this->accept();
|
||||
}
|
||||
|
||||
ui->widgetScanner->reset();
|
||||
if (ui->radio_clipboard->isChecked()) {
|
||||
Utils::copyToClipboard(QString::fromStdString(ui->widgetScanner->getURData()));
|
||||
Utils::showInfo(this, "Data copied to clipboard");
|
||||
}
|
||||
else if (ui->radio_file->isChecked()) {
|
||||
QString fn = QFileDialog::getSaveFileName(this, "Save to file", QDir::homePath(), "ur_data");
|
||||
if (fn.isEmpty()) {
|
||||
ui->widgetScanner->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
QFile file{fn};
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
Utils::showError(this, "Failed to save file", QString("Could not open file %1 for writing").arg(fn));
|
||||
ui->widgetScanner->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string data = ui->widgetScanner->getURData();
|
||||
file.write(data.data(), data.size());
|
||||
file.close();
|
||||
|
||||
Utils::showInfo(this, "Successfully saved data to file");
|
||||
}
|
||||
|
||||
ui->widgetScanner->reset();
|
||||
});
|
||||
|
||||
if (scanOnly) {
|
||||
ui->tabWidget->setCurrentIndex(1);
|
||||
ui->tabWidget->setTabVisible(0, false);
|
||||
ui->radio_clipboard->setVisible(false);
|
||||
ui->radio_file->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
ui->radio_file->setChecked(true);
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
|
||||
this->resize(600, 700);
|
||||
}
|
||||
|
||||
ViewOnlyDetails URDialog::getViewOnlyDetails() {
|
||||
return m_viewOnlyDetails;
|
||||
}
|
||||
|
||||
URDialog::~URDialog() = default;
|
|
@ -12,16 +12,26 @@ namespace Ui {
|
|||
class URDialog;
|
||||
}
|
||||
|
||||
struct ViewOnlyDetails {
|
||||
QString address;
|
||||
QString key;
|
||||
int restoreHeight = 0;
|
||||
QString walletName;
|
||||
};
|
||||
|
||||
class URDialog : public WindowModalDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit URDialog(QWidget *parent = nullptr);
|
||||
explicit URDialog(QWidget *parent, const QString &data = "", bool scanOnly = false);
|
||||
~URDialog() override;
|
||||
|
||||
ViewOnlyDetails getViewOnlyDetails();
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::URDialog> ui;
|
||||
ViewOnlyDetails m_viewOnlyDetails;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "URDialog.h"
|
||||
#include "utils/Utils.h"
|
||||
|
||||
ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
|
||||
|
@ -23,6 +24,10 @@ ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
|
|||
|
||||
connect(ui->btn_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboard);
|
||||
connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet);
|
||||
connect(ui->btn_transmitOverUR, &QPushButton::clicked, [this] {
|
||||
URDialog dialog{this, this->toString()};
|
||||
dialog.exec();
|
||||
});
|
||||
|
||||
if (m_wallet->viewOnly()) {
|
||||
ui->btn_Save->setEnabled(false);
|
||||
|
@ -52,12 +57,17 @@ void ViewOnlyDialog::onWriteViewOnlyWallet(){
|
|||
QMessageBox::information(this, "Information", "View-only wallet successfully written to disk.");
|
||||
}
|
||||
|
||||
void ViewOnlyDialog::copyToClipboard() {
|
||||
QString text = "";
|
||||
text += QString("Address: %1\n").arg(ui->label_primaryAddress->text());
|
||||
QString ViewOnlyDialog::toString() {
|
||||
QString text;
|
||||
text += QString("Secret view key: %1\n").arg(ui->label_secretViewKey->text());
|
||||
text += QString("Address: %1\n").arg(ui->label_primaryAddress->text());
|
||||
text += QString("Restore height: %1\n").arg(ui->label_restoreHeight->text());
|
||||
Utils::copyToClipboard(text);
|
||||
text += QString("Wallet name: %1\n").arg(m_wallet->walletName());
|
||||
return text;
|
||||
}
|
||||
|
||||
void ViewOnlyDialog::copyToClipboard() {
|
||||
Utils::copyToClipboard(this->toString());
|
||||
}
|
||||
|
||||
ViewOnlyDialog::~ViewOnlyDialog() = default;
|
||||
|
|
|
@ -25,6 +25,7 @@ private slots:
|
|||
void onWriteViewOnlyWallet();
|
||||
|
||||
private:
|
||||
QString toString();
|
||||
void copyToClipboard();
|
||||
|
||||
QScopedPointer<Ui::ViewOnlyDialog> ui;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>659</width>
|
||||
<height>254</height>
|
||||
<height>260</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -15,13 +15,13 @@
|
|||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Restore height</string>
|
||||
<string>Secret view key</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_restoreHeight">
|
||||
<widget class="QLabel" name="label_secretViewKey">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
|
@ -53,13 +53,13 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<widget class="QGroupBox" name="groupBox_6">
|
||||
<property name="title">
|
||||
<string>Secret view key</string>
|
||||
<string>Restore height</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_secretViewKey">
|
||||
<widget class="QLabel" name="label_restoreHeight">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
|
@ -87,6 +87,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_transmitOverUR">
|
||||
<property name="text">
|
||||
<string>Transmit over UR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -233,6 +233,14 @@ std::string QrCodeScanWidget::getURData() {
|
|||
return data;
|
||||
}
|
||||
|
||||
std::string QrCodeScanWidget::getURType() {
|
||||
if (!m_decoder.is_success()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return m_decoder.expected_type().value_or("");
|
||||
}
|
||||
|
||||
QString QrCodeScanWidget::getURError() {
|
||||
if (!m_decoder.is_failure()) {
|
||||
return {};
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
|
||||
QString decodedString = "";
|
||||
std::string getURData();
|
||||
std::string getURType();
|
||||
QString getURError();
|
||||
|
||||
void startCapture(bool scan_ur = false);
|
||||
|
|
|
@ -50,6 +50,12 @@ void PageSetRestoreHeight::initializePage() {
|
|||
ui->line_creationDate->setText(creationDate.toString("yyyy-MM-dd"));
|
||||
this->onCreationDateEdited();
|
||||
}
|
||||
|
||||
if (m_fields->restoreHeight > 0) {
|
||||
ui->line_restoreHeight->setText(QString::number(m_fields->restoreHeight));
|
||||
this->onRestoreHeightEdited();
|
||||
this->completeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void PageSetRestoreHeight::onCreationDateEdited() {
|
||||
|
|
|
@ -42,6 +42,10 @@ void PageWalletFile::initializePage() {
|
|||
ui->line_walletName->setText(this->defaultWalletName());
|
||||
ui->check_defaultWalletDirectory->setVisible(false);
|
||||
ui->check_defaultWalletDirectory->setChecked(false);
|
||||
|
||||
if (!m_fields->walletName.isEmpty()) {
|
||||
ui->line_walletName->setText(m_fields->walletName);
|
||||
}
|
||||
}
|
||||
|
||||
bool PageWalletFile::validateWidgets(){
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "WalletWizard.h"
|
||||
#include "constants.h"
|
||||
#include "dialog/URDialog.h"
|
||||
#include "libwalletqt/WalletManager.h"
|
||||
|
||||
PageWalletRestoreKeys::PageWalletRestoreKeys(WizardFields *fields, QWidget *parent)
|
||||
|
@ -41,6 +42,17 @@ PageWalletRestoreKeys::PageWalletRestoreKeys(WizardFields *fields, QWidget *pare
|
|||
|
||||
connect(ui->btnOptions, &QPushButton::clicked, this, &PageWalletRestoreKeys::onOptionsClicked);
|
||||
connect(ui->combo_walletType, &QComboBox::currentTextChanged, this, &PageWalletRestoreKeys::showInputLines);
|
||||
connect(ui->btn_scanUR, &QPushButton::clicked, [this] {
|
||||
URDialog dialog{this, "", true};
|
||||
dialog.exec();
|
||||
ViewOnlyDetails details = dialog.getViewOnlyDetails();
|
||||
ui->line_address->setText(details.address);
|
||||
ui->line_address->setCursorPosition(0);
|
||||
ui->line_viewkey->setText(details.key);
|
||||
ui->line_viewkey->setCursorPosition(0);
|
||||
m_fields->restoreHeight = details.restoreHeight;
|
||||
m_fields->walletName = details.walletName;
|
||||
});
|
||||
}
|
||||
|
||||
void PageWalletRestoreKeys::initializePage() {
|
||||
|
|
|
@ -215,6 +215,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btn_scanUR">
|
||||
<property name="text">
|
||||
<string>Scan UR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
|
|
Loading…
Reference in a new issue