]> Nutra Git (v2) - gamesguru/feather.git/commitdiff
airgap: allow transmitting view-only details over UR
authortobtoht <tob@featherwallet.org>
Tue, 5 Dec 2023 21:06:48 +0000 (22:06 +0100)
committertobtoht <tob@featherwallet.org>
Wed, 6 Dec 2023 02:08:58 +0000 (03:08 +0100)
13 files changed:
src/MainWindow.cpp
src/MainWindow.ui
src/dialog/URDialog.cpp
src/dialog/URDialog.h
src/dialog/ViewOnlyDialog.cpp
src/dialog/ViewOnlyDialog.h
src/dialog/ViewOnlyDialog.ui
src/qrcode/scanner/QrCodeScanWidget.cpp
src/qrcode/scanner/QrCodeScanWidget.h
src/wizard/PageSetRestoreHeight.cpp
src/wizard/PageWalletFile.cpp
src/wizard/PageWalletRestoreKeys.cpp
src/wizard/PageWalletRestoreKeys.ui

index 252b639090ab1a815e376c4c30847f34c30c4a32..dd2d1100926d65326d410227e5d473c1c07d59db 100644 (file)
@@ -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();
index 477e9f2b79e452e4bb2fe4bb47557fe219393492..e9b390e5e2533fcc2f9c3d184a3158df543100ce 100644 (file)
                 </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">
index 37444c6eb154f1ee021315c58b64cb877c8335eb..370f968899ed4d5cabe23af12ed154037fa2cadc 100644 (file)
@@ -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;
-           }
-
-           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 (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);
+
+            if (!match.hasMatch()) {
+                Utils::showError(this, "Unable to load view-only details", "Unexpected data");
+                return;
+            }
+
+            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"));
+
+            this->accept();
+        }
+
+        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;
\ No newline at end of file
index d7d871be7b8cfe7d1c334bc4fcf767d194b79f77..85aa965cd4593ca252cd34e65ad2042c91bdc2b0 100644 (file)
@@ -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;
 };
 
 
index 1ea454908d215ac2acc892b588d0782797effd12..364f6c4e9b2f1f451644fe0a64a7d7945553f255 100644 (file)
@@ -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;
index 08de23843c0378f08d97547a6556852c471b05e2..1ba44db3a7410ba83fc232eb08a3d35cf194f8df 100644 (file)
@@ -25,6 +25,7 @@ private slots:
     void onWriteViewOnlyWallet();
 
 private:
+    QString toString();
     void copyToClipboard();
 
     QScopedPointer<Ui::ViewOnlyDialog> ui;
index 2cdcdb8616aef1c625ad4370a1814b04ff7c5f2a..a09d9da8d8d644d7ec52986c1ded967bf2a733e8 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>659</width>
-    <height>254</height>
+    <height>260</height>
    </rect>
   </property>
   <property name="windowTitle">
   </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>
     </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>
        </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">
index 0c9b6e25f3ecae22d6694c79239a6939c5a193f2..39edf2d0cbb0f00a32769a986b7ae465ac47ca19 100644 (file)
@@ -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 {};
index 8c8b86f577bcdeab1e5b74fbaa94feefc211fe37..a239ba423096c928b28d9051bbba67de31d101d9 100644 (file)
@@ -29,6 +29,7 @@ public:
 
     QString decodedString = "";
     std::string getURData();
+    std::string getURType();
     QString getURError();
     
     void startCapture(bool scan_ur = false);
index 55d02ac04c9a204dd93628d6497589dedf682357..22eafba66e1e9c63094f30615f9c9776a8e22eb1 100644 (file)
@@ -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() {
index 6acc60c48f5df1f661c7b3991dfdc3d3e88cd162..83a70c447c7c8093e72e2bc5c1c6f1389c2a5b1a 100644 (file)
@@ -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(){
index 4ae57398fa791e80aaf4bb457b65dfa7b8642c87..ba69415578cb9d794e54950617dfab3e858a2407 100644 (file)
@@ -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() {
index 21b9f05698372384b7302efa626b96334d075b79..716c06fc2318650cf63968db342fd712d49c1eb5 100644 (file)
        </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">