walletframe.cpp raw

   1  // Copyright (c) 2011-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <qt/walletframe.h>
   6  
   7  #include <node/interface_ui.h>
   8  #include <psbt.h>
   9  #include <qt/guiutil.h>
  10  #include <qt/overviewpage.h>
  11  #include <qt/psbtoperationsdialog.h>
  12  #include <qt/walletmodel.h>
  13  #include <qt/walletview.h>
  14  #include <util/fs.h>
  15  #include <util/fs_helpers.h>
  16  
  17  #include <cassert>
  18  #include <fstream>
  19  #include <string>
  20  
  21  #include <QApplication>
  22  #include <QClipboard>
  23  #include <QGroupBox>
  24  #include <QHBoxLayout>
  25  #include <QLabel>
  26  #include <QPushButton>
  27  #include <QVBoxLayout>
  28  
  29  WalletFrame::WalletFrame(const PlatformStyle* _platformStyle, QWidget* parent)
  30      : QFrame(parent),
  31        platformStyle(_platformStyle),
  32        m_size_hint(OverviewPage{platformStyle, nullptr}.sizeHint())
  33  {
  34      // Leave HBox hook for adding a list view later
  35      QHBoxLayout *walletFrameLayout = new QHBoxLayout(this);
  36      setContentsMargins(0,0,0,0);
  37      walletStack = new QStackedWidget(this);
  38      walletFrameLayout->setContentsMargins(0,0,0,0);
  39      walletFrameLayout->addWidget(walletStack);
  40  
  41      // hbox for no wallet
  42      QGroupBox* no_wallet_group = new QGroupBox(walletStack);
  43      QVBoxLayout* no_wallet_layout = new QVBoxLayout(no_wallet_group);
  44  
  45      QLabel *noWallet = new QLabel(tr("No wallet has been loaded.\nGo to File > Open Wallet to load a wallet.\n- OR -"));
  46      noWallet->setAlignment(Qt::AlignCenter);
  47      no_wallet_layout->addWidget(noWallet, 0, Qt::AlignHCenter | Qt::AlignBottom);
  48  
  49      // A button for create wallet dialog
  50      QPushButton* create_wallet_button = new QPushButton(tr("Create a new wallet"), walletStack);
  51      connect(create_wallet_button, &QPushButton::clicked, this, &WalletFrame::createWalletButtonClicked);
  52      no_wallet_layout->addWidget(create_wallet_button, 0, Qt::AlignHCenter | Qt::AlignTop);
  53      no_wallet_group->setLayout(no_wallet_layout);
  54  
  55      walletStack->addWidget(no_wallet_group);
  56  }
  57  
  58  WalletFrame::~WalletFrame() = default;
  59  
  60  void WalletFrame::setClientModel(ClientModel *_clientModel)
  61  {
  62      this->clientModel = _clientModel;
  63  
  64      for (auto i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) {
  65          i.value()->setClientModel(_clientModel);
  66      }
  67  }
  68  
  69  bool WalletFrame::addView(WalletView* walletView)
  70  {
  71      if (!clientModel) return false;
  72  
  73      if (mapWalletViews.contains(walletView->getWalletModel())) return false;
  74  
  75      walletView->setClientModel(clientModel);
  76      walletView->showOutOfSyncWarning(bOutOfSync);
  77  
  78      WalletView* current_wallet_view = currentWalletView();
  79      if (current_wallet_view) {
  80          walletView->setCurrentIndex(current_wallet_view->currentIndex());
  81      } else {
  82          walletView->gotoOverviewPage();
  83      }
  84  
  85      walletStack->addWidget(walletView);
  86      mapWalletViews[walletView->getWalletModel()] = walletView;
  87  
  88      return true;
  89  }
  90  
  91  void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
  92  {
  93      if (!mapWalletViews.contains(wallet_model)) return;
  94  
  95      // Stop the effect of hidden widgets on the size hint of the shown one in QStackedWidget.
  96      WalletView* view_about_to_hide = currentWalletView();
  97      if (view_about_to_hide) {
  98          QSizePolicy sp = view_about_to_hide->sizePolicy();
  99          sp.setHorizontalPolicy(QSizePolicy::Ignored);
 100          view_about_to_hide->setSizePolicy(sp);
 101      }
 102  
 103      WalletView *walletView = mapWalletViews.value(wallet_model);
 104      assert(walletView);
 105  
 106      // Set or restore the default QSizePolicy which could be set to QSizePolicy::Ignored previously.
 107      QSizePolicy sp = walletView->sizePolicy();
 108      sp.setHorizontalPolicy(QSizePolicy::Preferred);
 109      walletView->setSizePolicy(sp);
 110      walletView->updateGeometry();
 111  
 112      walletStack->setCurrentWidget(walletView);
 113  
 114      Q_EMIT currentWalletSet();
 115  }
 116  
 117  void WalletFrame::removeWallet(WalletModel* wallet_model)
 118  {
 119      if (!mapWalletViews.contains(wallet_model)) return;
 120  
 121      WalletView *walletView = mapWalletViews.take(wallet_model);
 122      walletStack->removeWidget(walletView);
 123      delete walletView;
 124  }
 125  
 126  void WalletFrame::removeAllWallets()
 127  {
 128      QMap<WalletModel*, WalletView*>::const_iterator i;
 129      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 130          walletStack->removeWidget(i.value());
 131      mapWalletViews.clear();
 132  }
 133  
 134  bool WalletFrame::handlePaymentRequest(const SendCoinsRecipient &recipient)
 135  {
 136      WalletView *walletView = currentWalletView();
 137      if (!walletView)
 138          return false;
 139  
 140      return walletView->handlePaymentRequest(recipient);
 141  }
 142  
 143  void WalletFrame::showOutOfSyncWarning(bool fShow)
 144  {
 145      bOutOfSync = fShow;
 146      QMap<WalletModel*, WalletView*>::const_iterator i;
 147      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 148          i.value()->showOutOfSyncWarning(fShow);
 149  }
 150  
 151  void WalletFrame::gotoOverviewPage()
 152  {
 153      QMap<WalletModel*, WalletView*>::const_iterator i;
 154      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 155          i.value()->gotoOverviewPage();
 156  }
 157  
 158  void WalletFrame::gotoHistoryPage()
 159  {
 160      QMap<WalletModel*, WalletView*>::const_iterator i;
 161      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 162          i.value()->gotoHistoryPage();
 163  }
 164  
 165  void WalletFrame::gotoReceiveCoinsPage()
 166  {
 167      QMap<WalletModel*, WalletView*>::const_iterator i;
 168      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 169          i.value()->gotoReceiveCoinsPage();
 170  }
 171  
 172  void WalletFrame::gotoSendCoinsPage(QString addr)
 173  {
 174      QMap<WalletModel*, WalletView*>::const_iterator i;
 175      for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i)
 176          i.value()->gotoSendCoinsPage(addr);
 177  }
 178  
 179  void WalletFrame::gotoSignMessageTab(QString addr)
 180  {
 181      WalletView *walletView = currentWalletView();
 182      if (walletView)
 183          walletView->gotoSignMessageTab(addr);
 184  }
 185  
 186  void WalletFrame::gotoVerifyMessageTab(QString addr)
 187  {
 188      WalletView *walletView = currentWalletView();
 189      if (walletView)
 190          walletView->gotoVerifyMessageTab(addr);
 191  }
 192  
 193  void WalletFrame::gotoLoadPSBT(bool from_clipboard)
 194  {
 195      std::vector<unsigned char> data;
 196  
 197      if (from_clipboard) {
 198          std::string raw = QApplication::clipboard()->text().toStdString();
 199          auto result = DecodeBase64(raw);
 200          if (!result) {
 201              Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
 202              return;
 203          }
 204          data = std::move(*result);
 205      } else {
 206          QString filename = GUIUtil::getOpenFileName(this,
 207              tr("Load Transaction Data"), QString(),
 208              tr("Partially Signed Transaction (*.psbt)"), nullptr);
 209          if (filename.isEmpty()) return;
 210          if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
 211              Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
 212              return;
 213          }
 214          std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
 215          data.assign(std::istreambuf_iterator<char>{in}, {});
 216  
 217          // Some psbt files may be base64 strings in the file rather than binary data
 218          std::string b64_str{data.begin(), data.end()};
 219          b64_str.erase(b64_str.find_last_not_of(" \t\n\r\f\v") + 1); // Trim trailing whitespace
 220          auto b64_dec = DecodeBase64(b64_str);
 221          if (b64_dec.has_value()) {
 222              data = b64_dec.value();
 223          }
 224      }
 225  
 226      util::Result<PartiallySignedTransaction> psbt_res = DecodeRawPSBT(MakeByteSpan(data));
 227      if (!psbt_res) {
 228          Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(util::ErrorString(psbt_res).original), CClientUIInterface::MSG_ERROR);
 229          return;
 230      }
 231  
 232      auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
 233      dlg->openWithPSBT(*psbt_res);
 234      GUIUtil::ShowModalDialogAsynchronously(dlg);
 235  }
 236  
 237  void WalletFrame::encryptWallet()
 238  {
 239      WalletView *walletView = currentWalletView();
 240      if (walletView)
 241          walletView->encryptWallet();
 242  }
 243  
 244  void WalletFrame::backupWallet()
 245  {
 246      WalletView *walletView = currentWalletView();
 247      if (walletView)
 248          walletView->backupWallet();
 249  }
 250  
 251  void WalletFrame::changePassphrase()
 252  {
 253      WalletView *walletView = currentWalletView();
 254      if (walletView)
 255          walletView->changePassphrase();
 256  }
 257  
 258  void WalletFrame::unlockWallet()
 259  {
 260      WalletView *walletView = currentWalletView();
 261      if (walletView)
 262          walletView->unlockWallet();
 263  }
 264  
 265  void WalletFrame::usedSendingAddresses()
 266  {
 267      WalletView *walletView = currentWalletView();
 268      if (walletView)
 269          walletView->usedSendingAddresses();
 270  }
 271  
 272  void WalletFrame::usedReceivingAddresses()
 273  {
 274      WalletView *walletView = currentWalletView();
 275      if (walletView)
 276          walletView->usedReceivingAddresses();
 277  }
 278  
 279  WalletView* WalletFrame::currentWalletView() const
 280  {
 281      return qobject_cast<WalletView*>(walletStack->currentWidget());
 282  }
 283  
 284  WalletModel* WalletFrame::currentWalletModel() const
 285  {
 286      WalletView* wallet_view = currentWalletView();
 287      return wallet_view ? wallet_view->getWalletModel() : nullptr;
 288  }
 289