walletframe.cpp
1 // Copyright (c) 2011-2022 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.count(walletView->getWalletModel()) > 0) 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.count(wallet_model) == 0) 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.count(wallet_model) == 0) 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 std::string error; 227 PartiallySignedTransaction psbtx; 228 if (!DecodeRawPSBT(psbtx, MakeByteSpan(data), error)) { 229 Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR); 230 return; 231 } 232 233 auto dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel); 234 dlg->openWithPSBT(psbtx); 235 GUIUtil::ShowModalDialogAsynchronously(dlg); 236 } 237 238 void WalletFrame::encryptWallet() 239 { 240 WalletView *walletView = currentWalletView(); 241 if (walletView) 242 walletView->encryptWallet(); 243 } 244 245 void WalletFrame::backupWallet() 246 { 247 WalletView *walletView = currentWalletView(); 248 if (walletView) 249 walletView->backupWallet(); 250 } 251 252 void WalletFrame::changePassphrase() 253 { 254 WalletView *walletView = currentWalletView(); 255 if (walletView) 256 walletView->changePassphrase(); 257 } 258 259 void WalletFrame::unlockWallet() 260 { 261 WalletView *walletView = currentWalletView(); 262 if (walletView) 263 walletView->unlockWallet(); 264 } 265 266 void WalletFrame::usedSendingAddresses() 267 { 268 WalletView *walletView = currentWalletView(); 269 if (walletView) 270 walletView->usedSendingAddresses(); 271 } 272 273 void WalletFrame::usedReceivingAddresses() 274 { 275 WalletView *walletView = currentWalletView(); 276 if (walletView) 277 walletView->usedReceivingAddresses(); 278 } 279 280 WalletView* WalletFrame::currentWalletView() const 281 { 282 return qobject_cast<WalletView*>(walletStack->currentWidget()); 283 } 284 285 WalletModel* WalletFrame::currentWalletModel() const 286 { 287 WalletView* wallet_view = currentWalletView(); 288 return wallet_view ? wallet_view->getWalletModel() : nullptr; 289 }